pkusc 快到了……做点题涨涨 rp。

ref我好菜啊QAQ。


可以发现期望只是一个幌子。我们的目的是:对于所有随机的选择方法(一共 \(\binom{2n}{m}\)种),这些选择方法都最优地打出 \(k\) 张牌,他们能造成的伤害的和是多少。

显然的是,能打强化就打强化(不过你好歹也要攻击一张)。记 \(m\) 张卡中分给强化卡的数量为 \(i\)。我们枚举 \(i\),根据 \(i\) 与 \(k\) 的大小关系来决定怎样打牌。

那么 \(i < k\) 时,就打出 \(i\) 张强化卡,\(k-i\) 张攻击卡。(这意味着你能打的强化卡总共才 \(i\) 张,当然是能打强化卡就打强化卡)

\(i \geq k-1\) 时,就打出 \(k-1\) 张强化卡,\(1\) 张攻击卡。(这意味着你能打的强化卡还挺多,留一张攻击就行了)。

记 \(F(i,j)\) 为分给强化卡的数量为 \(i\),打出 \(j\) 张,所有方案翻的倍率的和。\(G(i,j)\) 为分给攻击卡的数量为 \(i\),打出 \(j\) 张,所有方案的(不加强化的)攻击力和。

两者分别对应 \(F(i,i) \times G(m-i,k-i)\) 和 \(F(i,k-1) \times G(m-i,1)\)。为什么可以是这种“和乘和”的形式呢?因为乘法分配律。


现在的问题变成快速计算 \(F\) 和 \(G\)。

关于 \(F\),可以 sort 以后定义一个 \(f\),\(f(i,j)\) 表示(注意是选,不是分)了 \(i\) 张强化牌且最这 \(i\) 张牌中位置靠前的那张牌是所有强化牌中的第 \(j\) 个,这样的所有方案翻的倍率的和。转移看代码。\(G\) 和 \(g\) 也类似。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int mod=998244353;
int T, n, m, k, a[1505], b[1505], c[3005][3005], f[1505][1505];
int g[1505][1505], sum[1505];
int F(int x, int y){
if(x<y) return 0;
if(!y) return c[n][x];
int re=0;
for(int j=x-y+1; j<=n-y+1; j++)
re = (re + (ll)f[y][j]*c[j-1][x-y]%mod) % mod;
//感性理解一下……大概就是把x-y张不打出的牌放到j前头,这里我讲不太清QAQ
return re;
}
int G(int x, int y){
if(x<y) return 0;
int re=0;
for(int j=x-y+1; j<=n-y+1; j++)
re = (re + (ll)g[y][j]*c[j-1][x-y]%mod) % mod;
return re;
}
int main(){
cin>>T;
for(int i=0; i<=3000; i++){
c[i][0] = 1;
for(int j=1; j<=i; j++)
c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
}
while(T--){
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
scanf("%d %d %d", &n, &m, &k);
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
for(int i=1; i<=n; i++) scanf("%d", &b[i]);
sort(a+1, a+1+n);//跟牌的顺序无关,可以sort
sort(b+1, b+1+n);
for(int i=1; i<=n; i++){
f[1][i] = a[i];//初始化f[][],显然只选1张的倍率之和是a[i]
sum[i] = (sum[i-1] + a[i]) % mod;//前缀和,方便转移
}
for(int i=2; i<=n; i++){
for(int j=1; j<=n-i+1; j++)
f[i][j] = (ll)a[j] * (sum[n]-sum[j]+mod) % mod;
//打了i张牌,最前头的是第j张,那它就是f[i-1][j+1..n]的和再乘上第j号元素。这个转移的思想是枚举在打了i-1张牌的时候最前头的是哪一张
for(int j=1; j<=n; j++)
sum[j] = (sum[j-1] + f[i][j]) % mod;
}
for(int i=1; i<=n; i++){
g[1][i] = b[i];
sum[i] = (sum[i-1] + b[i]) % mod;
}
for(int i=2; i<=n; i++){
for(int j=1; j<=n-i+1; j++)
g[i][j] = ((ll)b[j]*c[n-j][i-1]%mod+(sum[n]-sum[j]+mod)%mod) % mod;
//打了i张牌,最前头的是第j张。注意g代表的是(不加强化的)攻击力和。在这种情况下,打了i-1张牌的总情况是c[n-j][i-1]种(j+1..n中选i-1个的方案数),这是第一项;第二项就是继承自g[i-1][j+1..n]
for(int j=1; j<=n; j++)
sum[j] = (sum[j-1] + g[i][j]) % mod;
}
int ans=0;
for(int i=0; i<m; i++)
if(i<k) ans = (ans + (ll)F(i,i)*G(m-i,k-i)%mod) % mod;
else ans = (ans + (ll)F(i,k-1)*G(m-i,1)%mod) % mod;
printf("%d\n", ans);
}
return 0;
}

loj2538 「PKUWC 2018」Slay the Spire的更多相关文章

  1. LOJ #2538. 「PKUWC 2018」Slay the Spire (期望dp)

    Update on 1.5 学了 zhou888 的写法,真是又短又快. 并且空间是 \(O(n)\) 的,速度十分优秀. 题意 LOJ #2538. 「PKUWC 2018」Slay the Spi ...

  2. LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp + FMT)

    写在这道题前面 : 网上的一些题解都不讲那个系数是怎么推得真的不良心 TAT (不是每个人都有那么厉害啊 , 我好菜啊) 而且 LOJ 过的代码千篇一律 ... 那个系数根本看不出来是什么啊 TAT ...

  3. LOJ #2541. 「PKUWC 2018」猎人杀(容斥 , 期望dp , NTT优化)

    题意 LOJ #2541. 「PKUWC 2018」猎人杀 题解 一道及其巧妙的题 , 参考了一下这位大佬的博客 ... 令 \(\displaystyle A = \sum_{i=1}^{n} w_ ...

  4. LOJ #2540. 「PKUWC 2018」随机算法(概率dp)

    题意 LOJ #2540. 「PKUWC 2018」随机算法 题解 朴素的就是 \(O(n3^n)\) dp 写了一下有 \(50pts\) ... 大概就是每个点有三个状态 , 考虑了但不在独立集中 ...

  5. 【loj2538】 【PKUWC 2018】Slay the Spire dp

    我们不难发现,假设抽了x张攻击牌,y张强化牌,那么肯定是打出尽可能多张的强化牌后,再开始出攻击牌(当然最少要一张攻击牌) 我们设G(i,j)表示:所有(抽到的攻击牌牌数为i,打出的攻击牌牌数为j)的方 ...

  6. LOJ #2537. 「PKUWC 2018」Minimax (线段树合并 优化dp)

    题意 小 \(C\) 有一棵 \(n\) 个结点的有根树,根是 \(1\) 号结点,且每个结点最多有两个子结点. 定义结点 \(x\) 的权值为: 1.若 \(x\) 没有子结点,那么它的权值会在输入 ...

  7. 「PKUWC 2018」Minimax

    传送门:Here 一道线段树合并好题 如果要维护点$ x$的信息,相当于合并$ x$的两棵子树 对于这题显然有:任何叶子节点的权值都可能出现在其祖先上 因而我们只需要在线段树合并的时候维护概率即可 我 ...

  8. loj2540 「PKUWC 2018」随机算法

    pkusc 快到了--做点题涨涨 rp. 记 \(f(S,i)\) 表示 \(S\) 这个集合是决计不能选的(要么属于独立集,要么和独立集相连),或称已经考虑了的,\(i\) 表示此集合对应的最大独立 ...

  9. 「PKUWC 2018」随机算法 (第二版,正解做法)

    上一版貌似是打了 O(3 ^ N) 暴力和 一条链的情况,得了60分.... 第一次做的时候光想练一练暴力...就没去想正解,谁知道正解比暴力好写不知道多少,mmp 设 f(S) 为 选集合S中的点可 ...

随机推荐

  1. 【CSS】iconfont的使用

    说到浏览器对@font-face的兼容问题,这里涉及到一个字体format的问题,因为不同的浏览器对字体格式支持是不一致的,这样大家有必要了解一下,各种版本的浏览器支持什么样的字体,前面也简单带到了有 ...

  2. [Python 多线程] 详解daemon属性值None,False,True的区别 (五)

    本文以多个例子介绍Python多线程中daemon属性值的区别. 回顾: 前面的文章简单介绍了在现代操作系统中,每一个进程都认为自己独占所有的计算机资源. 或者说线程就是独立的王国,进程间是相对独立的 ...

  3. VIM在Win7上的安装教程

    1.下载 目前VIM在其官网上的最新版本为7.4,Windows版本名称为GVIM,在百度软件中可以下载GVIM的最新版本,建议 在百度上下载,因为比较快.在百度上搜索"GVIM" ...

  4. MFC窗体程序中添加调试控制台

    在编写复杂程序的过程中,我们经常需要将一些信息输出到文件或者屏幕上.较控制台应用程序,MFC窗体程序要显得麻烦一些! 下面有2种方法来实现为MFC窗体程序添加调试控制台,方便程序员调试程序和了解当前程 ...

  5. pem文件转p12

    p12->pem cer.p12: openssl pkcs12 -clcerts -nokeys -out cer.pem -in cer.p12 key.p12: openssl pkcs1 ...

  6. 使用Docker遇到的基本命令及问题小结

    当遇到Cannot connect to the Docker daemon. Is the docker daemon running on this host?导致Docker无法启动时,重启Do ...

  7. 一个loser的忏悔

    一直认为自己是世界的主角,从小意气用事,耽误了学业,现在才发现了自己的爱好,于是开始努力进阶. 愿不辜负自己的努力! 高中大学青葱旺盛的美好时期全部用在了感叹人生上,只能在30岁的年纪重新扛起学业,活 ...

  8. Java接口和抽象类详解

    父类定义了相关子类的共有属性和行为.而接口可以定义类的共同行为(包括非相关的类). 了解接口前,先来说说抽象类.抽象类介乎于普通类和接口之间,提供部分实现方法以及未实现方法,可以看作为一个半成品. 抽 ...

  9. C++继承与多态练习--计算图形面积

    1.目的: /*设计一个计算图形面积的类库. 类库的顶层是一个抽象类,并且提供三个纯虚函数:显示数据成员.返回面积和返回体积. Class Shape { virtual void showData( ...

  10. MySQL学习【第十一篇存储引擎之事务解释】

    一.innodb的核心特点------事务 1.什么是事务 在一组数据操作执行步骤,这些步骤被视为一个单元,主要针对dml语句(update.delete.insert) 2.事务ACID特性 Ato ...