【省选模拟】—猎人杀(概率dp)
和加特林轮盘赌差别不大(虽然我没做过……)
考场上想了一个利用树状图转移的做法
但是觉得写不出来就没做…
实际上可以很简单的列出式
表示剩个人,第个人活下来的概率
发现这是带环的
而且多个环相互独立的
手动消元就可以了
#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
#define int long long
inline char gc(){
static char ibuf[RLEN],*ib,*ob;
(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ib==ob)?EOF:*ib++;
}
#define gc getchar
const int mod=1e9+7,inv=5e8+4;
inline int read(){
char ch=gc();
int res=0,f=1;
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
return f?res:-res;
}
inline int add(int a,int b){
return a+b>=mod?a+b-mod:a+b;
}
inline int mul(int a,int b){
return a*b>=mod?a*b%mod:a*b;
}
inline int dec(int a,int b){
return a>=b?a-b:a-b+mod;
}
inline int ksm(int a,int b,int res=1){
for(;b;b>>=1,a=mul(a,a))(b&1)?(res=mul(res,a)):0;
return res;
}
const int N=2005;
struct data{
int x,y;
data(int _x=0,int _y=0):x(_x),y(_y){}
friend inline data operator +(const data &a,const data &b){
return data(a.x+b.x,a.y+b.y);
}
friend inline data operator+(const data&a,const int&b){
return a+data(0,b);
}
friend inline data operator *(const data &a,int b){
return data(mul(a.x,b),mul(a.y,b));
}
}g[N];
bool vis[N];
int f[N][N],k,idx[N];
inline int pre(int i,int j){
return j==k?i:(j>k?j-k:j-k+i);
}
inline int nxt(int i,int j){
return j+k<=i?j+k:j+k-i;
}
inline void calc(int n,int m){
vis[m]=1,idx[1]=m;
if(m!=k)g[1]=data(inv,mul(inv,f[n-1][pre(n,m)]));
else g[1]=data(inv,0);
int p=nxt(n,m),pr=m,tim=2,x0;
while(!vis[p]){
idx[tim]=p,vis[p]=1;
if(p!=k)g[tim]=(g[tim-1]+f[n-1][pr])*inv;
else g[tim]=g[tim-1]*inv;
pr=p,p=nxt(n,p),++tim;
}
--tim;
x0=mul(g[tim].y,ksm(dec(1,g[tim].x),mod-2));
for(int i=1;i<=tim;i++)f[n][idx[i]]=add(mul(g[i].x,x0),g[i].y);
}
int n,K;
signed main(){
n=read(),K=read();
f[1][1]=1;
for(int i=2;i<=n;i++){
memset(vis,0,sizeof(vis));
k=(K%i==0)?i:(K%i);
for(int j=1;j<=i;j++)if(!vis[j])calc(i,j);
}
cout<<f[n][1];
}