机房训练赛----四微生物的游戏

这是一道老道的期望题

我被高斯消元迷惑了双眼

只想到了O(n^3)的做法

我们将1号击杀0号转化为0号自杀

会发现问题变得更容易思考一点

dp[i][j]代表当还剩i个人时,如果第0个人率先自杀

所有人胜利的概率

那么接下来我们先计算每个人有多少的概率自杀

可以发现传递的方式会形成一个环

共有gcd(n,i)个环

每个环的大小就是i/gcd(n,i)

对于这个环里的第j个元素(从0开始编号

他的这个概率是首项为i/c*(1-i/c)^j,公比为(1-i/c)^(i/gcd(n,i))的等比数列

求一个和就可以了

然后转移到的每一个自杀的人,对于现在的贡献就是temp[i]*dp[i-1]的序列中的每个元素

依次累加到后面的每个人上面

就基本上结束了

接下来就是正解时间(琪亚娜时间)

在DP之前,有个小技巧:为了便于表示状态,我们可以认为位置固定为0,但枪的位置丌定。另外,我们可以认为枪的位置在其真实位置-1的地斱,然后每个人向自巪开枪。
•DP[i][j]表示还剩i个人,枪在编号为j的人手上的获胜概率。

DP[i][j]=DP[i-1][j mod i]*i/C+DP[i][(j+k)mod(i+1)]*(1-i/C)
•但是这样DP会出现问题,就是转移斱程会形成环。

可以用高斯消元

O(n^4)也是六十分的一种算法

但是

在同一个循环中,可以发现以下的DP式:Cir[k]表示当前循环的第k个。令C[0]=j
DP[i][Cir[0]]=DP[i-1][Cir[k]]*(i/C)*(1-i/C)^k+DP[i][Cir[0]]*(1-i/C)^{i/GCD(i,k)}

因此循环一圈后,可以算出最初的一个数

借此可以推出这个循环内其他所有数的值

但是还需要注意一个点

当dp[i][j]中j为0时 他已经击杀了自己 所以第一项没有任何的贡献

然后就可以得出答案了

此外还有很多种dp定义和转移

我也不太清楚

在这里

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+9;
int temp[1005],dp[1005][1005],inv[1005],t,n,c,k,invk,invc,vis[1005];
int fast(int x,int y)
{
	int now=1;
	while(y)
	{
		if(y&1)
		{
			now=1ll*now*x%mod;
		}
		y>>=1;
		x=1ll*x*x%mod;
	}
	return now;
}
int gcd(int x,int y)
{
	if(y==0) return x;
	return gcd(y,x%y);
	
}
int moc(int x)
{
	if(x>=mod) return x-mod;
	return x;
}
int main()
{
	freopen("gun.in","r",stdin);	
	freopen("gun.out","w",stdout);
	inv[0]=inv[1]=1;
	for(int i=2;i<=1000;i++)
	inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d",&n,&c,&k);
		memset(dp,0,sizeof(dp));
		dp[1][0]=1;
		for(int i=2;i<=n;i++)
		{
			int lql=i/gcd(k,i);
			int piece=gcd(k,i);
			memset(vis,0,sizeof(vis));
			int a1=1ll*(i-1)*inv[c]%mod;
			int q=1ll*(c-i+1)*inv[c]%mod;
			int invv=fast(q,mod-2);
			int zyl=fast(moc(1-fast(q,lql)+mod),mod-2);
			for(int j=0;j<i;j++)
			if(!vis[j])
			{
				int here=a1;
				for(int g=j,l=1;l<=lql;l++,g+=k)
				{
					g%=i;
					vis[g]=1;
			//		if(g!=0)
						dp[i][j]=moc(dp[i][j]+1ll*here*dp[i-1][g%(i-1)]%mod);
					here=1ll*here*q%mod;
				}
				if(j==0)
				{
					dp[i][j]=moc(dp[i][j]-1ll*dp[i-1][0]*(i-1)%mod*inv[c]%mod+mod);
				}
				dp[i][j]=1ll*dp[i][j]*zyl%mod;
				for(int g=(j+k)%i,l=1,r=j;l<lql;l++,g+=k,r+=k)
				{
					g%=i;
					r%=i;
					if(r==0)
						dp[i][g]=1ll*dp[i][r]*invv%mod;
					else
						dp[i][g]=1ll*moc(dp[i][r]-1ll*dp[i-1][r%(i-1)]*(i-1)%mod*inv[c]%mod+mod)*invv%mod;
				}
			}
		}
		for(int i=0,j=1;j<=n;j++,i--)
		{
			if(i<0) i+=n;
			printf("%d ",moc(dp[n][i]+mod));
		}
		printf("\n");
	}
	return 0;
}
/*
3
3 3 1
2 2 2 
4 5 3
*/

放一下代码

猜你喜欢

转载自blog.csdn.net/ENESAMA/article/details/82946920