版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88258262
洛谷传送门
BZOJ传送门
解析:
除了最后解方程需要一点技巧以外,剩下的就是套路啊。
先来看一个一般一点的问题,给出一个图(非DAG),询问 到 的期望步数。
显然一个点的期望是
列方程高斯消元就行了。
现在回到这道题:
先判掉不用列方程的无解的情况。
然后考虑 次伤害,显然受到的伤害可以用一个 串来表示,则受到恰好 次伤害的 串有 种,总概率为
可以递推出来。
现在开始列方程,对于 ,我们需要考虑受到治疗的情况,则有
对于
,它不可能受到治疗,所以:
就可以愉快的高斯消元 TLE了。
1500怎么玩 的高斯消元?
我们发现这是一个下三角矩阵,于是每次消元只会用到当前行有效的两个数字,把最后一个for循环去掉即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define cs const
cs int mod=1e9+7;
inline int add(cs int &a,cs int &b){return a+b>=mod?a+b-mod:a+b;}
inline int Add(int &a,cs int &b){return a=add(a,b);}
inline int dec(cs int &a,cs int &b){return a<b?a-b+mod:a-b;}
inline int Dec(int &a,cs int &b){return a=dec(a,b);}
inline int mul(cs int &a,cs int &b){return (ll)a*b%mod;}
inline int Mul(int &a,cs int &b){return a=mul(a,b);}
inline int quickpow(int a,int b,int res=1){
while(b){
if(b&1)Mul(res,a);
Mul(a,a);
b>>=1;
}
return res;
}
cs int N=1505;
int ma[N][N];
int n,p,m,k,ans;
int P[N];
inline void solve(){
scanf("%d%d%d%d",&n,&p,&m,&k);
if(k==0){cout<<"-1\n";return ;}
if(m==0){
if(k==1){cout<<"-1\n";return ;}
ans=0;
while(p>0){
if(p<n)++p;
p-=k;
++ans;
}
cout<<ans<<"\n";
return ;
}
int invm=quickpow(m,mod-2),inv1=quickpow(m+1,mod-2);
P[0]=quickpow(mul(m,inv1),k);
for(int re i=1;i<=min(n,k);++i)P[i]=mul(P[i-1],mul(mul(invm,k-i+1),quickpow(i,mod-2)));
for(int re i=1;i<n;++i)
for(int re j=1;j<=i;++j)ma[i][j]=mul(add(mul(P[i-j],m),P[i-j+1]),inv1);
for(int re i=1;i<n;++i)ma[i][i+1]=mul(P[0],inv1);
for(int re i=1;i<n;++i)Dec(ma[i][i],1);
for(int re i=1;i<=n;++i)ma[n][i]=P[n-i];
Dec(ma[n][n],1);
for(int re i=1;i<=n;++i)ma[i][n+1]=mod-1;
for(int re i=1;i<=n;++i){
int inv=quickpow(ma[i][i],mod-2);
ma[i][i]=1;
Mul(ma[i][n+1],inv);
if(i<n)Mul(ma[i][i+1],inv);
for(int re j=i+1;j<=n;++j){
int mult=ma[j][i];
ma[j][i]=0;
Dec(ma[j][i+1],mul(mult,ma[i][i+1]));
Dec(ma[j][n+1],mul(mult,ma[i][n+1]));
}
}
for(int re i=n;i>1;--i)Dec(ma[i-1][n+1],mul(ma[i-1][i],ma[i][n+1])),ma[i-1][i]=0;
cout<<ma[p][n+1]<<"\n";
}
inline void init(){
for(int re i=1;i<=min(n,k);++i)P[i]=0;
for(int re i=1;i<=n;++i)
for(int re j=1;j<=n+1;++j)ma[i][j]=0;
}
int T;
signed main(){
for(scanf("%d",&T);T--;){
init();
solve();
}
return 0;
}