【题目】
原题地址
题目大意见原题
【解题思路】
首先可以发现我们一定是能用加强就先加强。然后加强和攻击一定是从大到小打的。
我们记 m m m张卡中强化有 i i i张,则当 i < k i<k i<k,打 i i i张强化和 k − i k-i k−i张攻击,当 i ≥ k − 1 i\geq k-1 i≥k−1,打 k − 1 k-1 k−1张强化和 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(m−i,k−i)和 F ( i , k − 1 ) × G ( m − i , 1 ) F(i,k-1)\times G(m-i,1) F(i,k−1)×G(m−i,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<j∑f(i−1,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×(i−1j−1)+p<j∑g(i−1,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=1∑nf(y,i)×(x−yn−i)
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=1∑ng(y,i)×(x−yn−i)
特别地,对于 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的好题。