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. python迭代器与生成器详解

    迭代器与生成器 迭代器(iterator)与生成器(generator)是 Python 中比较常用又很容易混淆的两个概念,今天就把它们梳理一遍,并举一些常用的例子. for 语句与可迭代对象(ite ...

  2. require()  module.export    Object.keys()

    import API from"../../api/api.js";   var data = require('../../utils/data.js').songs;   // ...

  3. nginx的锁

    一.原理 nginx的锁是基于共享内存实现的,这点跟redis中利用一个存储(也就是一个键值对)来实现锁的原理是一致的,每一项操作通过检查锁对象的lock域是否为0,来判断能否获取锁并尝试获取锁. 二 ...

  4. python装饰器同时支持有参数和无参数的练习题

    ''' 预备知识: …… @decorator def f(*args,**kwargs): pass # 此处@decorator  等价于 f = decorator(f) @decorator2 ...

  5. SAwUML – UML-based, contractual software architectures and their formal analysis using SPIN

    一.基本信息 标题:SAwUML – UML-based, contractual software architectures and their formal analysis using SPI ...

  6. openXML设置Excel行组合

    重要代码: row = new DOS.Row() { RowIndex = new DOX.UInt32Value(rowIndex), Spans = new DOX.ListValue<D ...

  7. Apache启动不成功时,用命令行检测(新手)

    1,在配置Apache服务器时,经常要在httpd.conf 修改和添加一些代码,编写中,误写或者写错时,无法正常启动时,直接报错The requested operation has failed! ...

  8. windows mysql zip 安装

    https://www.cnblogs.com/iathanasy/p/8461429.html

  9. 《python语言程序设计》_第4章_选择

    第四章 # 4.1 引言 布尔表达式:选择语句选择的条件. 程序: import math #加载math模块radius=eval(input("Enter an integer:&quo ...

  10. Promise实践

    一.概念 Promise是异步编程的解决方案之一,与事件驱动+回调函数并列. Promise是专门为异步编程设计的封闭的一次性用品,封闭体现在只有异步操作的结果能改变其状态,其他任何操作都不能改变其状 ...