【DP or 生成函数】[CodeForces - 712D]Memory and Scores
题目大意
Memory 和 Lexa分别取数,取t轮,每一轮取的数字的范围为
分析
算法1 :DP
很容易想到计算一个人在游戏开始后得分的动态规划。
令
我们只需要对
最后,我们枚举Memory在游戏开始后的得分,显然,只要Lexa的得分
我们只需要对
由于下标可能是负数,我们平移一下坐标。
代码
ps:代码和题解略有不同,代码使用的刷表法
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXT 100
#define MAXK 1000
#define MOD 1000000007
using namespace std;
void Read(int &x){
static char c;
bool f(0);
while(c=getchar(),c!=EOF){
if(c=='-')
f=1;
else if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
if(f)
x=-x;
return;
}
}
}
int f[MAXT+1][MAXT*MAXK*2+10],a,b,t,jz=100000,k,ans;
int main()
{
Read(a),Read(b),Read(k),Read(t);
int i,j;
f[0][jz]=1;
for(i=1;i<=t;i++){
for(j=0;j<=jz*2;j++)
if(f[i-1][j]){
f[i][j-k]=(f[i][j-k]+f[i-1][j])%MOD;
f[i][j+k+1]=(f[i][j+k+1]-f[i-1][j]+MOD)%MOD;
}
for(j=1;j<=jz*2;j++)
f[i][j]=(f[i][j]+f[i][j-1])%MOD;
}
for(j=1;j<=jz*2;j++)
f[t][j]=(f[t][j]+f[t][j-1])%MOD;
for(j=0;j<=jz*2;j++){
int tt=min(jz*2,j+a-b-1);
if(tt<0)
continue;
else
ans=(ans+1ll*(f[t][j]-f[t][j-1])*f[t][tt])%MOD;
}
printf("%d\n",ans);
}
算法2:生成函数
我们考虑上一个DP,如果我们一直计算到
那么每一层的生成函数就是
最后
第一个因式的系数我们可以用二项式定理直接算出每一项的系数,第二个式子我们可以根据生成函数每一项所代表的意义算出。
当差值
我们可以对第二个因式的系数求一个前缀和,枚举第一个因式的每一项,然后开这一项和第二个因式的哪些项乘起来指数在
当然,你也可以直接FFT
时间复杂度都是预处理阶乘和逆元的复杂度
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXT 100
#define MAXK 1000
#define MOD 1000000007
using namespace std;
int a,b,k,t,jz,fac[MAXT*MAXK*4+1000],inv[MAXT*MAXK*4+1000],ans;
void Read(int &x){
char c;
while(c=getchar(),c!=EOF){
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
}
int quick_pow(int a,int b){
int ret(1);
while(b){
if(b&1)
ret=1ll*ret*a%MOD;
a=1ll*a*a%MOD;
b>>=1;
}
return ret;
}
void prepare(){
fac[0]=inv[0]=1;
int i;
for(i=1;i<=k*t*4+2*t;i++){
fac[i]=1ll*fac[i-1]*i%MOD;
inv[i]=quick_pow(fac[i],MOD-2);
}
}
inline int C(int n,int m){
return 1ll*fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int fz[MAXT*2+10],fm[MAXT*MAXK*4+10];
int main()
{
Read(a),Read(b),Read(k),Read(t);
jz=k*t;
prepare();
int l=2*k*t+b-a+1,r=4*k*t,i;
if(l>r){
puts("0");
return 0;
}
for(i=0;i<=2*t;i++)
fz[i]=(((i&1)?-1:1)*C(2*t,i)+MOD)%MOD;
for(i=0;i<=4*k*t;i++)
fm[i]=C(2*t-1+i+1,2*t); //这个经过了组合数化简,所以求的其实是前缀和
for(i=0;i<=2*t;i++){
int nl=l-(2*k+1)*i,nr=r-(2*k+1)*i;
if(nr<0)
break;
if(nl<0)
ans=(ans+1ll*fz[i]*fm[nr])%MOD;
else
ans=(ans+1ll*fz[i]*(fm[nr]-fm[nl-1]+MOD))%MOD;
}
printf("%d\n",ans);
}