【dp-有针对性设计方案】LOJ2538 [PKUWC2018] Slay the Spire

【题目】
原题地址
题目大意见原题

【解题思路】
首先可以发现我们一定是能用加强就先加强。然后加强和攻击一定是从大到小打的。
我们记 m m m张卡中强化有 i i i张,则当 i < k i<k i<k,打 i i i张强化和 k − i k-i ki张攻击,当 i ≥ k − 1 i\geq k-1 ik1,打 k − 1 k-1 k1张强化和 1 1 1张攻击。
我们记 F ( i , j ) F(i,j) F(i,j)为选 i i i张强化,打出 j j j张,所有方案翻倍的倍率和, G ( i , j ) G(i,j) G(i,j)为选 i i i张攻击,所有方案造成伤害和,那么上面的两种情况实际上分别对应 F ( i , i ) × G ( m − i , k − i ) F(i,i)\times G(m-i,k-i) F(i,i)×G(mi,ki) F ( i , k − 1 ) × G ( m − i , 1 ) F(i,k-1)\times G(m-i,1) F(i,k1)×G(mi,1)(由乘法分配律可知是对的)
我们现在要求的就是这两个数组。
f ( i , j ) f(i,j) f(i,j)为用了 i i i张强化,最后一张是第 j j j张所有方案倍率的和, g ( i , j ) g(i,j) g(i,j)类似,那么:
f ( i , j ) = w j × ∑ p < j f ( i − 1 , p ) f(i,j)=w_j\times \sum_{p<j}f(i-1,p) f(i,j)=wj×p<jf(i1,p)
g ( i , j ) = w j × ( j − 1 i − 1 ) + ∑ p < j g ( i − 1 , p ) g(i,j)=w_j\times {j-1\choose i-1}+\sum_{p<j}g(i-1,p) g(i,j)=wj×(i1j1)+p<jg(i1,p)
其中 g g g的转移要乘上组合数是因为它会贡献个若干个方案,而不是一个,而 f f f的贡献实际上就是乘上了一个数。
那么最终我们可以得到 F F F G G G
F ( x , y ) = ∑ i = 1 n f ( y , i ) × ( n − i x − y ) F(x,y)=\sum_{i=1}^n f(y,i)\times {n-i \choose x-y} F(x,y)=i=1nf(y,i)×(xyni)
G ( x , y ) = ∑ i = 1 n g ( y , i ) × ( n − i x − y ) G(x,y)=\sum_{i=1}^n g(y,i)\times {n-i \choose x-y} G(x,y)=i=1ng(y,i)×(xyni)
特别地,对于 y = 0 y=0 y=0 F ( x , y ) = ( n x ) F(x,y)={n\choose x} F(x,y)=(xn)

【参考代码】

#include
using namespace std;

typedef long long ll;
const int N=3005,mod=998244353;
int n,m,K,ans;
int lup[N],atk[N],sum[N];
int f[N][N],g[N][N],c[N][N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

int C(int x,int y){if(y>x)return 0;return c[x][y];}
int mul(int x,int y){return (ll)x*y%mod;}
int upm(int x){return x>=mod?x-mod:x;}
void up(int &x,int y){x+=y;if(x>=mod)x-=mod;}

void init()
{
	for(int i=0;i<N;++i) 
	{
		c[i][0]=c[i][i]=1;
		for(int j=1;j<i;++j) c[i][j]=upm(c[i-1][j]+c[i-1][j-1]);
	}
}

int getf(int x,int y)
{
	if(x<y) return 0; if(!y) return c[n][x];
	int ret=0;
	for(int i=1;i<=n;++i) up(ret,mul(f[y][i],C(n-i,x-y)));
	return ret;
}

int getg(int x,int y)
{
	if(x<y || !y) return 0; 
	int ret=0;
	for(int i=1;i<=n;++i) up(ret,mul(g[y][i],C(n-i,x-y)));
	return ret;
}

bool cmp(int x,int y){return x>y;}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("LOJ2538.in","r",stdin);
	freopen("LOJ2538.out","w",stdout);
#endif
	init();
	int T=read();
	while(T--)
	{
		n=read();m=read();K=read();
		for(int i=1;i<=n;++i) lup[i]=read();
		for(int i=1;i<=n;++i) atk[i]=read();
		sort(lup+1,lup+n+1,cmp);sort(atk+1,atk+n+1,cmp);

		sum[0]=0;
		for(int i=1;i<=n;++i)
		{
			if(i==1) 
			{
				for(int j=1;j<=n;++j) f[i][j]=lup[j];
				for(int j=1;j<=n;++j) sum[j]=upm(sum[j-1]+f[i][j]);
				continue;
			}
			for(int j=1;j<=n;++j) f[i][j]=mul(lup[j],sum[j-1]);
			for(int j=1;j<=n;++j) sum[j]=upm(sum[j-1]+f[i][j]);
		}
		for(int i=1;i<=n;++i)
		{
			if(i==1)
			{
				for(int j=1;j<=n;++j) g[i][j]=atk[j];
				for(int j=1;j<=n;++j) sum[j]=upm(sum[j-1]+g[i][j]);
				continue;
			}
			for(int j=1;j<=n;++j) g[i][j]=upm(sum[j-1]+mul(atk[j],C(j-1,i-1)));
			for(int j=1;j<=n;++j) sum[j]=upm(sum[j-1]+g[i][j]);
		}

		ans=0;
		for(int i=0;i<=min(n,m);++i)
		{
			int j=m-i;if(j>n || j<0) continue;
			if(i<K) up(ans,mul(getf(i,i),getg(j,K-i)));
			else up(ans,mul(getf(i,K-1),getg(j,1)));
		}
		printf("%d\n",ans);
	}

	return 0;
}

【总结】
这是一道针对结果设计dp的好题。

你可能感兴趣的:(DP-组合计数)