[LOJ2538][PKUWC2018]Slay the Spire:DP

分析

学会新姿势!我们可以通过调整DP顺序来体现选取物品的优先顺序!

显然选取强化牌的最优策略是倍数从高到低,能选就选,最多选\(k-1\)张,选取攻击牌的最优策略是伤害从高到低,尽量少选,但最少选\(1\)张。

我们可以把强化牌从大到小排序,把攻击牌从小到大排序,令\(f[i][j]\)表示考虑了最大的\(i\)张强化牌,其中所有可选的强化牌有\(j\)张的情况的最优策略下的强化倍数和,\(g[i]\)表示考虑了最小的\(i\)张攻击牌,其中所有可选的攻击牌有\(j\)张的情况的最优策略下的伤害和。

状态转移方程如下:

\[f[i][j]=f[i-1][j]+f[i-1][j-1] \times a[i]\ (j \leq k-1) \]

\[f[i][j]=f[i-1][j]+f[i-1][j-1]\ (j > k-1) \]

\[g[i][j]=b[i] \times \binom{i-1}{j-1}\ (m-j \geq k-1) \]

\[g[i][j]=b[i] \times \binom{i-1}{j-1}+g[i-1][j-1]\ (m-j < k-1) \]

代码

#include <bits/stdc++.h>

#define rin(i,a,b) for(int i=(a);i<=(b);++i)
#define irin(i,a,b) for(int i=(a);i>=(b);--i)
#define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
#define Size(a) (int)a.size()
#define pb push_back
#define mkpr std::make_pair
#define fi first
#define se second
#define lowbit(a) ((a)&(-(a)))
typedef long long LL;

using std::cerr;
using std::endl;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

const int MAXN=3005;
const int MOD=998244353;

int n,m,k,a[MAXN],b[MAXN],f[MAXN],g[MAXN];
int fac[MAXN],invf[MAXN];

inline int qpow(int x,int y){
	int ret=1,tt=x%MOD;
	while(y){
		if(y&1)ret=1ll*ret*tt%MOD;
		tt=1ll*tt*tt%MOD;
		y>>=1;
	}
	return ret;
}

inline int binom(int n,int m){
	if(n<0||m<0||n<m)return 0;
	return 1ll*fac[n]*invf[n-m]%MOD*invf[m]%MOD;
}

void init(){
	fac[0]=1;rin(i,1,n)fac[i]=1ll*fac[i-1]*i%MOD;
	invf[n]=qpow(fac[n],MOD-2);irin(i,n-1,0)invf[i]=1ll*invf[i+1]*(i+1)%MOD;
}

int main(){
	n=3000;init();
	int T=read();
	while(T--){
		n=read(),m=read(),k=read();
		rin(i,1,n)a[i]=read();
		rin(i,1,n)b[i]=read();
		std::sort(a+1,a+n+1);
		std::sort(b+1,b+n+1); 
		rin(i,0,m)f[i]=g[i]=0;
		f[0]=1,g[0]=0;
		irin(i,n,1)irin(j,std::min(n-i+1,m),1){
			if(j<=k-1)f[j]=(f[j]+1ll*f[j-1]*a[i])%MOD;
			else f[j]=(f[j]+f[j-1])%MOD;
		}
		rin(i,1,n)irin(j,std::min(i,m),1){
			if(m-j<k-1)g[j]=(g[j]+1ll*binom(i-1,j-1)*b[i]+g[j-1])%MOD;
			else g[j]=(g[j]+1ll*binom(i-1,j-1)*b[i])%MOD;
		}
		int ans=0;
		rin(i,0,m)ans=(ans+1ll*f[i]*g[m-i])%MOD;
		printf("%d\n",ans);
	}
	return 0;
}

posted on 2019-05-09 09:56  ErkkiErkko  阅读(246)  评论(0编辑  收藏  举报