Description

现在有 \(n\) 张强化牌和 \(n\) 张攻击牌:

  1. 攻击牌:打出后对对方造成等于牌上的数字的伤害。
  2. 强化牌:打出后,假设该强化牌上的数字为 \(x\),则其他剩下的攻击牌的数字都会乘上 \(x\)。保证强化牌上的数字都大于 1

现在等概率地从这 \(2n\) 张卡中抽出 \(m\) 张,并且按最优策略打出 \(k\) 张,问期望能造成多少伤害。

Solution

第一这是披着期望皮的计数题

第二最优策略肯定是能打强化牌就打强化牌,最后剩下一张攻击牌再打

既然是计数题那我们就要算出每种方案造成的伤害值然后加起来就行了

设 \(f(i,j,0/1)\) 表示前 \(i\) 张强化牌用 $j $ 张,第 \(i\) 张用不用的所有方案的乘积的和,\(g(i,j,0/1)\) 表示前 \(i\) 张攻击牌用 \(j\) 张,第 \(i\) 张用不用的所有方案的和的和

那么有转移 \(f(i,j,0)=f(i-1,j,0)+f(i-1,j,1),f(i,j,1)=f(i,j-1,0)\cdot a[i]\)

\(g(i,j,0)=g(i-1,j,0)+g(i-1,j,1),g(i,j,1)=g(i,j-1,0)+b[i]\cdot C_{i-1}^{j-1}\)

再设 \(F(i,j)\) 表示选出 \(i\) 张强化牌,用了 \(j\) 张的所有方案的乘积的和,\(G(i,j)\) 表示选出 \(i\) 张攻击牌,用了 \(j\) 张的所有方案的和的和。

那么显然有 \(ans=\sum\limits_{i=0}^m \begin{cases}F(i,i)\times G(m-i,m-i)\;\;(i<k)\\F(i,k-1)\times G(m-i,1)\;\;(i\ge k)\end{cases}\)

而 \(F,G\) 也可以通过 \(f,g\) 来求出来,可以枚举断点,即用的第 \(j\) 张是原序列的第 \(x\) 张,那么因为选择的是最优策略,原序列的前 \(x\) 张中一定不多不少选出来了 \(j\) 张,那剩下的 \(i-j\) 张就要从 \(x+1\sim n\) 中选出来,拿组合数算一下就行了。具体就是 \(F(i,j)=\sum\limits_{x=j}^{n-(i-j)} f(x,j,1)\times C_{n-x}^{i-j}\)

发现求这个 \(F,G\) 是 \(O(n^3)\) 的并不能过去。但是我们也没有必要求出所有的 \(F,G\) 。只求出需要求的那 \(O(n)\) 项即可。

Code

LOJ格式化代码好丑

#include<bits/stdc++.h>
using std::min;
using std::max;
using std::swap;
using std::vector;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)
#define int long long
const int N=3005;
const int mod=998244353;
#define inv(x) ksm(x,mod-2) int a[N],b[N];
int n,m,k,C[N][N];
int f[N][N][2],g[N][N][2]; int getint(){
int X=0,w=0;char ch=getchar();
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
if(w) return -X;return X;
} void init(int n){
C[0][0]=1;
for(int i=1;i<=n;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
} int F(int x,int y){
int ans=0;
for(int i=y;i<=n;i++)
(ans+=f[i][y][1]*C[n-i][x-y]%mod)%=mod;
return ans;
} int G(int x,int y){
if(!x) return 0;
int ans=0;
for(int i=y;i<=n;i++)
(ans+=g[i][y][1]*C[n-i][x-y]%mod)%=mod;
return ans;
} void solve(){
n=getint(),m=getint(),k=getint();
for(int i=1;i<=n;i++) a[i]=getint();
for(int i=1;i<=n;i++) b[i]=getint();
memset(f,0,sizeof f);memset(g,0,sizeof g);
std::sort(a+1,a+1+n),std::sort(b+1,b+1+n);
std::reverse(a+1,a+1+n),std::reverse(b+1,b+1+n);
if(k==1){
int sum=0;
for(int i=1;i<=n;i++){
if(2*n-i+1<m) break;
(sum+=b[i]*C[2*n-i][m-1]%mod)%=mod;
} printf("%lld\n",sum);return;
} f[0][0][1]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
f[i][j][0]=(f[i-1][j][0]+f[i-1][j][1])%mod;
g[i][j][0]=(g[i-1][j][0]+g[i-1][j][1])%mod;
if(j) f[i][j][1]=f[i][j-1][0]*a[i]%mod;
if(j) g[i][j][1]=(g[i][j-1][0]+b[i]*C[i-1][j-1]%mod)%mod;
}
} int ans=0;
for(int i=0;i<=m and i<=n;i++){
if(m-i>n) continue;
if(i<k) (ans+=F(i,i)*G(m-i,k-i)%mod)%=mod;
else (ans+=F(i,k-1)*G(m-i,1)%mod)%=mod;
} printf("%lld\n",ans);
} signed main(){
init(3000);
int T=getint();
while(T--) solve();
return 0;
}

[PKUWC2018] Slay the spire的更多相关文章

  1. BZOJ.5467.[PKUWC2018]Slay the Spire(DP)

    LOJ BZOJ 洛谷 哪张能力牌能乘攻击啊,太nb了叭 显然如果有能力牌,那么应该选最大的尽可能的打出\(k-1\)张. 然后下面说的期望都是乘总方案数后的,即所有情况的和.然后\(w_i\)统一用 ...

  2. 题解-PKUWC2018 Slay the Spire

    Problem loj2538 Solution 在考场上当然要学会写暴力,考虑如果手上已经有了\(a\)张攻击牌和\(b\)张强化牌: 首先强化牌会在攻击牌之前用(废话),其次要将两种牌分别从大往小 ...

  3. LOJ2538 PKUWC2018 Slay the Spire DP

    传送门 不想放题面了,咕咕咕咕咕 这个期望明明是用来吓人的,其实要算的就是所有方案的最多伤害的和. 首先可以知道的是,能出强化牌就出强化牌(当然最后要留一张攻击牌出出去),且数字尽量大 所以说在强化牌 ...

  4. [LOJ2538] [PKUWC2018] Slay the Spire

    题目链接 LOJ:https://loj.ac/problem/2538 Solution 计数好题. 首先可以发现这题和期望没关系. 其次对于手上的一套牌,设我们有\(a\)张强化牌,那么: 如果\ ...

  5. 【洛谷5299】[PKUWC2018] Slay the Spire(组合数学)

    点此看题面 大致题意: 有\(n\)张强化牌\(a_i\)和\(n\)张攻击牌\(b_i\),每张牌有一个权值(强化牌的权值大于\(1\)),每张强化牌能使所有攻击牌的权值乘上这张强化牌的权值,每张攻 ...

  6. [LOJ2538][PKUWC2018]Slay the Spire:DP

    分析 学会新姿势!我们可以通过调整DP顺序来体现选取物品的优先顺序! 显然选取强化牌的最优策略是倍数从高到低,能选就选,最多选\(k-1\)张,选取攻击牌的最优策略是伤害从高到低,尽量少选,但最少选\ ...

  7. 洛谷 P5299 - [PKUWC2018]Slay the Spire(组合数学+dp)

    题面传送门 hot tea 啊--这种风格及难度的题放在省选 D2T1 左右还是挺喜闻乐见的罢 首先考虑对于固定的 \(m\) 张牌怎样求出最优的打牌策略,假设我们抽到了 \(p\) 张强化牌,攻击力 ...

  8. loj #2538. 「PKUWC2018」Slay the Spire

    $ \color{#0066ff}{ 题目描述 }$ 九条可怜在玩一个很好玩的策略游戏:Slay the Spire,一开始九条可怜的卡组里有 \(2n\) 张牌,每张牌上都写着一个数字\(w_i\) ...

  9. BZOJ 5467 Slay the Spire

    BZOJ 5467 Slay the Spire 我的概率基础也太差了.jpg 大概就是这样,因为强化牌至少翻倍,所以打出的牌必定是全部的强化牌或者$k-1$个强化牌,然后剩余的机会打出最大的几个攻击 ...

随机推荐

  1. xmlhttprequest readyState 属性的五种状态

    关于readystate五个状态总结如下: readyState 状态    状态说明(0)未初始化此阶段确认XMLHttpRequest对象是否创建,并为调用open()方法进行未初始化作好准备.值 ...

  2. linux之配置Mongodb~

       OK 让我们先下载一波mongodb~(64位ubuntu) curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0. ...

  3. Firewalld的结构

    原文地址:http://www.excelib.com/article/287/show firewalld简介 Centos7中默认将原来的防火墙iptables升级为了firewalld,fire ...

  4. Android插件化的兼容性(中):Android P的适配

    Android系统的每次版本升级,都会对原有代码进行重构,这就为插件化带来了麻烦. Android P对插件化的影响,主要体现在两方面,一是它重构了H类中Activity相关的逻辑,另一个是它重构了I ...

  5. 剑指offer面试题24:二叉搜索树的后序遍历序列

    题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是返回true,否则返回false. 假设输入的数组任意两个数字都不相同 解题思路:二叉搜索树的特点是根节点的左子树的值小于等 ...

  6. Android 自定义 View 绘制

    在 Android 自定义View 里面,介绍了自定义的View的基本概念.同时在 Android 控件架构及View.ViewGroup的测量 里面介绍了 Android 的坐标系 View.Vie ...

  7. PHPExcel防止大数以科学计数法显示

    在使用PHPExcel来进行数据导出时,常常需要防止有些数字(如手机号.身份证号)以科学计数法显示,我们可以采用下面的方式来解决: setCellValueExplicit第三个参数用PHPExcel ...

  8. 应用监控CAT之cat-consumer源码阅读(二)

    之前讲了 cat-client 进行cat埋点上报,那么上报给谁呢?以及后续故事如何?让我们来看看 cat-consumer 是如何接收处理的? 由cat-client发送数据,cat-consume ...

  9. MySql事务的隔离级别及作用

    逻辑工作单元遵循一系列(ACID)规则则称为事务. 原子性:保证事务是一系列的运作,如果中间过程有一个不成功则全部回滚,全部成功则成功.保证了事务的原则性. 一致性:一致性指的是比如A向B转100块钱 ...

  10. 机器学习入门01 - 框架处理(Framing)

    原文链接:https://developers.google.com/machine-learning/crash-course/framing (监督式)机器学习的定义:机器学习系统通过学习如何组合 ...