这是一道老道的期望题
我被高斯消元迷惑了双眼
只想到了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
*/
放一下代码