【题目】
原题地址
题目大意见原题
【解题思路】
首先可以发现我们一定是能用加强就先加强。然后加强和攻击一定是从大到小打的。
我们记
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<bits/stdc++.h>
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的好题。