测试地址:Slay the Spire
做法:本题需要用到DP+组合数。
题目要求的就是对于所有抽牌的方案,能得到的伤害值的总和。
我们知道,在使用的强化牌一定的基础上,攻击牌肯定是从大到小取最优。然后要观察到一个结论:如果有强化牌,先出强化牌是最优的,出到没有强化牌,或者只有一张牌可出的时候再出攻击牌。如何证明?因为强化牌的倍数,令出强化牌之前的可出攻击牌数值和为,最小的可出攻击牌数值为,因为要多出一张强化牌,所以就不能出了,那么多出强化牌比不出能打出的伤害要多,即,因为且(因为是最小值),所以多出强化牌总是最优的,得证。
于是我们就自然而然有了一个状态定义,令为在所有张强化牌的方案中取最大张能得到的倍数之和,为在所有张攻击牌的方案中取最大张能得到的伤害之和,有:
问题就是如何求和。
以强化牌为例,我们枚举最后出的牌中数值最小的那一张,所以我们先将牌从大到小排序,然后令为前张强化牌中取张,且第张必须取,能得到的倍数之和,有:
用前缀和即可优化到。那么有:
对于攻击牌也差不多,也是从大到小排序,然后令为前张攻击牌中取张,且第张必须取,能得到的伤害之和,有:
同样用前缀和优化到。那么有:
于是在计算时,我们要求次和,每次求是,而预处理组合数的时间复杂度是的,所以总的时间复杂度是,可以通过此题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
int T,n,m,k,last=0;
ll wg[1510],wf[1510],C[1510][1510]={0};
ll g[1510][1510],f[1510][1510],sum[1510][1510],sumf[1510][1510];
bool cmp(ll a,ll b)
{
return a>b;
}
void calc_C(int n)
{
if (n<=last) return;
for(int i=last+1;i<=n;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;
}
last=n;
}
ll G(int x,int y)
{
if (x>n||x<y) return 0;
if (y==0) return C[n][x];
ll ans=0;
for(int i=1;i<=n;i++)
ans=(ans+g[i][y]*C[n-i][x-y])%mod;
return ans;
}
ll F(int x,int y)
{
if (x>n||x<y) return 0;
ll ans=0;
for(int i=1;i<=n;i++)
ans=(ans+f[i][y]*C[n-i][x-y])%mod;
return ans;
}
int main()
{
scanf("%d",&T);
C[0][0]=1;
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
calc_C(n);
for(int i=1;i<=n;i++)
scanf("%lld",&wg[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&wf[i]);
sort(wg+1,wg+n+1,cmp);
sort(wf+1,wf+n+1,cmp);
for(int i=0;i<=n+1;i++)
g[0][i]=sum[0][i]=0;
g[0][0]=sum[0][0]=1;
for(int i=1;i<=n;i++)
{
g[i][0]=sum[i][0]=1;
for(int j=1;j<=i;j++)
{
if (j>k) break;
g[i][j]=wg[i]*sum[i-1][j-1]%mod;
sum[i][j]=(sum[i-1][j]+g[i][j])%mod;
}
sum[i][i+1]=0;
}
for(int i=0;i<=n+1;i++)
f[0][i]=sumf[0][i]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
if (j>k) break;
f[i][j]=(sumf[i-1][j-1]+wf[i]*C[i-1][j-1])%mod;
sumf[i][j]=(sumf[i-1][j]+f[i][j])%mod;
}
sumf[i][i+1]=0;
}
ll ans=0;
for(int i=0;i<=k-1;i++)
ans=(ans+G(i,i)*F(m-i,k-i))%mod;
for(int i=k;i<=m-1;i++)
ans=(ans+G(i,k-1)*F(m-i,1))%mod;
printf("%lld
",ans);
}
return 0;
}