解题思路
用(g[i])表示(k)次攻击后掉(i)滴血的概率,显然有(g[i]=C_{k}^{i}(frac{1}{m+1})^i(frac{m}{m+1})^{k-i})
用(f[i])表示英雄初始血量为(i)的期望存活轮数,那么显然有:
[f[i]=sum_{j=0}^{min(i+1,k)}(frac{1}{m+1})g[j]f[i+1-j]+sum_{j=0}^{j=min(i,k)}(frac{m}{m+1})g[j]f[i-j]+1
]
血量为(n)时不能加血,所以转移有所不同:
[f[n]=sum_{j=0}^{j=min(n,k)}g[j]f[n-j]+1
]
通过上面的第一个式子可以将(f[i])推到(f[i+1]),然后发现一个很大的问题(f[1])并不确定
所以我们将(f[1])设为(X),那么(f)的每个状态都类似(aX+b)的形式,然后跟第二个式子解出(X)的值即可.
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1505,tt=1e9+7;
struct jz{
int x,y;
jz(int x=0,int y=0):x(x),y(y){}
jz operator+(const jz &b)const{return jz((x+b.x)%tt,(y+b.y)%tt);}
jz operator-(const jz &b)const{return jz((x-b.x+tt)%tt,(y-b.y+tt)%tt);}
jz operator*(int w)const{return jz((LL)x*w%tt,(LL)y*w%tt);}
}f[maxn],ans;
int g[maxn],n,p,m,k,T;
int qsm(int w,int b){int num=1;while(b){if (b&1) num=(LL)num*w%tt;w=(LL)w*w%tt;b>>=1;}return num;}
int work1(){
if(k==1&&n!=1) return -1;
g[0]=0;for(int i=1;i<n;i++) g[i]=g[max(i+1-k,0)]+1;
g[n]=g[max(n-k,0)]+1;
return g[p];
}
int work(){
scanf("%d%d%d%d",&n,&p,&m,&k);int Invm=qsm(m+1,tt-2);
if (!k) return -1;if (!m) return work1();
int C=1;for (int i=0;i<=min(k,n);i++) g[i]=(LL)C*qsm(Invm,i)%tt*qsm((LL)m*Invm%tt,k-i)%tt,C=(LL)C*(k-i)%tt*qsm(i+1,tt-2)%tt;
if (g[0]==1||g[0]==0) return -1;
f[0]=jz(0,0);f[1]=jz(1,0);
for (int i=1;i<n;i++){
f[i+1]=(f[i]-jz(0,1))*(m+1);
for (int j=0;j<=min(i,k);j++) f[i+1]=f[i+1]-(f[i-j]*g[j])*m;
for (int j=1;j<=min(i+1,k);j++) f[i+1]=f[i+1]-(f[i+1-j]*g[j]);
f[i+1]=f[i+1]*qsm(g[0],tt-2);
}
ans=jz(0,1);for (int i=1;i<=min(n,k);i++) ans=ans+f[n-i]*g[i];
ans=ans*qsm(1-g[0]+tt,tt-2);ans=ans-f[n];
if (!ans.x&&f[p].x) return -1;int X=(LL)ans.y*qsm(tt-ans.x,tt-2)%tt;
return ((LL)f[p].x*X+f[p].y)%tt;
}
int main(){
freopen("exam.in","r",stdin);
freopen("exam.out","w",stdout);
scanf("%d",&T);while(T--) printf("%d
",work());return 0;
}