嘘~ 正在从服务器偷取页面 . . .

洛谷P5299 [PKUWC2018]Slay the Spire 题解


洛谷P5299 [PKUWC2018]Slay the Spire 题解

题目链接:P5299 [PKUWC2018]Slay the Spire

题意

九条可怜在玩一个很好玩的策略游戏:Slay the Spire,一开始九条可怜的卡组里有 $2n$ 张牌,每张牌上都写着一个数字$w_i$,一共有两种类型的牌,每种类型各 $n$ 张:

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

保证强化牌上的数字都大于 1

现在九条可怜会等概率随机从卡组中抽出 $m$ 张牌,由于费用限制,九条可怜最多打出 $k$ 张牌,假设九条可怜永远都会采取能造成最多伤害的策略,求她期望造成多少伤害。

假设答案为 $\text{ans}$ ,你只需要输出

即可。其中 $x!$ 表示 $\prod_{i=1}^{x}i$,特别地,$0!=1$ 。

输入格式

第一行一个正整数 $T$ 表示数据组数

接下来对于每组数据:

第一行三个正整数 $n,m,k$ 。

第二行 $n$ 个正整数 $w_i$,表示每张强化牌上的数值。

第三行 $n$ 个正整数 $w_i$,表示每张攻击牌上的数值。

输出格式

输出 $T$ 行,每行一个非负整数表示每组数据的答案。

数据范围

对于所有数据,有 $1\leq k\leq m\leq 2n\leq 3\times 10^3,~1\leq \sum 2n\leq 3000$,且 $1\leq w_i\leq 10^8$ 。

首先考虑最优出牌策略是什么。

记 $a_i$ 为强化牌,$b_i$ 为攻击牌,并且从大到小排序

根据贪心的套路,考虑什么时候会用强化牌来替换攻击牌。

设某个策略中使用了 $x$ 张强化牌,$y$ 张攻击牌。

我们将第 $y$ 张攻击牌(也就是最小的那张)换成第 $x+1$ 张强化牌。

如果和原先的策略比没有变劣,则

移项整理得

等式两侧除以 $\prod_{i=1}^{x}a_i$ ,得

由于题目中保证强化牌上的数字都大于 1,因此

又因为 $y>1$ 时 $b_i$ 从大到小排序过了,所以 $\sum_{i=1}^{y-1} b_i$ 大于等于 $b_y$ 恒成立。

因此在至少有一张攻击牌时,一定优先选择强化牌

接下来我们就可以开始dp了。状态设计的思路来源于计算答案的部分。

  1. $f_{i,j,0}$ 表示在前 $i$ 张强化牌中选择 $j$ 张,且第 $i$ 张被选中的所有情况中,这 $j$ 张牌的乘积之和
  2. $g_{i,j,0}$ 表示在前 $i$ 张攻击牌中选择 $j$ 张,且第 $i$ 张被选中的所有情况中,这 $j$ 张牌的和之和
  3. $f_{i,j,1}$ 定义为 $\sum_{t=1}^i f_{t, j, 0}$ ,前缀和优化。
  4. $g_{i,j,1}$ 定义为 $\sum_{t=1}^i g_{t, j, 0}$ ,前缀和优化。

考虑转移,这个还算比较简单

第一、三行可能比较难理解,因为这里表示的是所有情况的和

第一行,每个和都会乘上 $a_i$ ,根据乘法分配律,就等价于原来所有的和乘以 $a_i$ 。

第三行,这里的乘法是因为 $\binom{i-1}{j-1}$ 种选攻击牌的方案,每种都加上了权值 $b_i$ ,因此一共加上了 $b_i \times \binom{i-1}{j-1}$ 。


有了这些奇奇怪怪的dp式子后有啥用呢?

考虑统计答案,需要分类讨论(因为策略不同)。

第一种情况: $m$ 张牌中强化牌的数量小于 $k-1$ 张。

此时比较好处理,可以把所有强化牌都拿上,然后选较大的那些攻击牌就好了。

不难发现其实等于 $k-1$ 的也是满足的,但是其实在另一类中可以方便一些。

我们枚举 $i,j$ ,分别表示强化牌有 $i$ 张以及最后被选中的攻击牌是第 $j$ 张

在强化牌中选择 $i$ 张的所有合法情况的乘积之和,根据定义就是 $f_{n,i,1}$ 。

在攻击牌中选剩下的 $k-i$ 张,且最后一张一定是第 $j$ 张的所有合法情况的和之和,根据定义就是 $g_{j,k-i,0}$ 。

同时我们需要满足剩下的 $m-k$ 张牌

  • 没有强化牌,否则不满足假设
  • 所有攻击牌都在第 $j$ 张之后,否则不是最优策略

故方案数为 $\binom{n-j}{m-k}\times\binom{n-i}{0} = \binom{n-j}{m-k}$ 。

因此对于每个 $(i,j)$ ,贡献为

第二种情况:$m$ 张牌中,强化牌的数量大于等于 $k-1$ 。

此时必然是选择最大的 $k-1$ 张强化牌,然后选极大的那张攻击牌。

对于这种情况,我们枚举 $i,j$ ,分别表示最后被选中的强化牌是第 $i$ 张,且选中的攻击牌是第 $j$ 张。

在前 $i$ 张强化牌中选择 $k-1$ 张,且第 $i$ 张必须选,所有合法情况下的乘积之和,即 $f_{i,k-1,0}$ 。

选择第 $j$ 张攻击牌,则攻击牌之和就是 $b_j$ 。

同时我们需要满足剩下的 $m-k$ 张牌

  • 所有强化牌都在第 $i$ 张之后
  • 所有攻击牌都在第 $j$ 张之后

故方案数为 $\binom{2n-i-j}{m-k}$ 。

因此对于每个 $(i,j)$ ,贡献为

时间复杂度 $\mathcal{O}(Qn^2)$ ,有点卡常

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
const ll mod = 998244353;
#define mul3(a,b,c) ( (a) % mod * (b) % mod * (c) % mod )
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
void add(int &x,int y) { (x += y) >= mod ? x-=mod : 0; }
int Add(int x,int y) { return ((x+=y) >= mod) ? (x-=mod) : x; }
namespace FastIO
{
    #define gc() readchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        static T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
    template<typename T>void print(T k) { write(k); pc('\n'); }
}using namespace FastIO;
#define N ((int)(3e3+15))

int n,m,k,Q,res,a[N],b[N],C[N][N],f[N][N][2],g[N][N][2];
void init()
{
    C[0][0] = 1;
    for(int i=1; i<=N-5; i++)
    {
        C[i][0] = 1;
        for(int j=1; j<=i; j++)
            C[i][j] = Add(C[i-1][j-1], C[i-1][j]);
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    init(); for(read(Q); Q--; res = 0)
    {
        read(n); read(m); read(k);
        for(int i=1; i<=n; i++) read(a[i]); sort(a+1, a+1+n, greater<int>());
        for(int i=1; i<=n; i++) read(b[i]); sort(b+1, b+1+n, greater<int>());
        f[0][0][0] = f[0][0][1] = 1;
        for(int i=1; i<=n; i++)
        {
            f[i][0][1] = 1;
            for(int j=1; j<=i; j++)
            {
                f[i][j][0] = (ll)a[i] * f[i-1][j-1][1] % mod;
                f[i][j][1] = Add(f[i][j][0], f[i-1][j][1]);
                g[i][j][0] = Add((ll)b[i] * C[i-1][j-1] % mod, g[i-1][j-1][1]);
                g[i][j][1] = Add(g[i][j][0], g[i-1][j][1]);
            }
        }
        for(int i=0; i < k-1; i++)
            for(int j=1; j<=n; j++)
                add(res, mul3(f[n][i][1], g[j][k - i][0], C[n - j][m - k]));
        for(int i=0; i<=n; i++)
            for(int j=1; j<=n; j++)
                add(res, mul3(f[i][k-1][0], b[j], C[2*n - i - j][m - k]));
        print(res);
    }
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/blog/TheLostWeak/solution-p5299


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录