2019.03.06【BJOI2018】【洛谷P4458】【BZOJ5292】治疗之雨(高斯消元)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88258262

洛谷传送门

BZOJ传送门


解析:

除了最后解方程需要一点技巧以外,剩下的就是套路啊。

先来看一个一般一点的问题,给出一个图(非DAG),询问 S S T T 的期望步数。

显然一个点的期望是 E T = 0 , E u = 1 + v s u c ( u ) P u v E v ,E_T=0,E_u=1+\sum_{v\in suc(u)}P_{uv}E_v

列方程高斯消元就行了。

现在回到这道题:

先判掉不用列方程的无解的情况。

然后考虑 k k 次伤害,显然受到的伤害可以用一个 01 01 串来表示,则受到恰好 a a 次伤害的 01 01 串有 ( k a ) {k\choose a} 种,总概率为 P a = ( k a ) ( 1 m + 1 ) a ( m m + 1 ) k 1 P_a={k\choose a}(\frac{1}{m+1})^a(\frac{m}{m+1})^{k-1}

可以递推出来。

现在开始列方程,对于 i ! = n i!=n ,我们需要考虑受到治疗的情况,则有

E i = j = 1 i ( m m + 1 P i j + 1 m + 1 P i j + 1 E j ) + 1 m + 1 P 0 E i + 1 + 1 E_i=\sum_{j=1}^i(\frac{m}{m+1}P_{i-j}+\frac{1}{m+1}P_{i-j+1}E_j)+\frac{1}{m+1}P_0E_{i+1}+1

对于 n n ,它不可能受到治疗,所以:
E n = 1 + j = 1 n P n j E j E_n=1+\sum_{j=1}^nP_{n-j}E_j

就可以愉快的高斯消元 TLE了。

1500怎么玩 O ( n 3 ) O(n^3) 的高斯消元?

我们发现这是一个下三角矩阵,于是每次消元只会用到当前行有效的两个数字,把最后一个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;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/88258262