紙牌遊戲
阿新 • • 發佈:2018-11-01
題目描述
華華和秀秀在玩紙牌遊戲,遊戲的規則如下:
初始時,桌面上有n張紙牌,每張紙牌上寫有一個正整數。遊戲開始時華華先在黑板上寫上數字0,之後秀秀和華華輪流選取紙牌(秀秀先手)。當一個人選定一張紙牌時,他需要將黑板上的數字改寫成這個數和紙牌上的數的最大公約數,然後將這張紙牌丟棄。當一個人寫下了1 或者無法選取紙牌時,他就輸了。
現在秀秀想知道:
-
當華華和秀秀都按照隨機策略選取卡片時,秀秀獲勝的概率有多少;
-
當華華和秀秀都按照最優策略選取卡片時,秀秀獲勝的概率有多少。
題解
兩個問題是完全沒關係的兩道題。
對於前者,我們可以記取了
張牌當前黑板上東西是
的概率。
若
沒有變,說明你又取了一個
的倍數則
若
變成了
,
列舉一下暴力轉移就行了,寫的時候主動轉移更方便。統計答案如果下一次
變成了
則你贏。再判一下最後沒東西取的情況就可以了。
對於後一個問題,orzszm,可以看看她的題解orzszm
程式碼
#include <bits/stdc++.h>
#define maxn 1005
#define INF 0x3f3f3f3f
using namespace std;
int read(){
int res,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1; res=(c^48);
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res*f;
}
int n,num[maxn],gcd[maxn][maxn],sum[maxn],tot[maxn],mx;
double f[maxn][maxn],ans;
bool vis[maxn];
int _gcd(int a,int b){return b?_gcd(b,a%b):a;}
int main(){
n=read();
for(int i=1;i<=n;i++){
num[i]=read(),mx=max(mx,num[i]);
for(int j=1;j<=num[i];j++){
if(num[i]%j==0) sum[j]++;
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<=mx;j++){
gcd[i][j]=_gcd(num[i],j);
}
}
f[0][0]=1;
for(int i=0;i<n;i++){
for(int j=0;j<=mx;j++){
if(j^1) f[i+1][j]+=f[i][j]*(sum[j]-i)/(n-i);
for(int k=1;k<=n;k++){
if(gcd[k][j]^j) f[i+1][gcd[k][j]]+=f[i][j]/(n-i);
}
}
if(i&1) ans+=f[i+1][1];
}
if(n&1) for(int i=2;i<=mx;i++) ans+=f[n][i];
printf("%.9lf ",ans);
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
vis[gcd[i][num[j]]]=1;
}
}
for(int i=2;i<=mx;i++){
if(vis[i]) for(int j=2*i;j<=mx;j+=i) vis[j]=0;
}
for(int i=2;i<=mx;i++){
if(vis[i]) for(int j=1;j<=n;j++) tot[i]+=(num[j]%i==0);
}
vis[1]=0;
for(int i=1;i<=n;i++){
bool flag=1;
for(int j=1;j*j<=num[i];j++){
if(num[i]%j==0){
if(vis[j]) flag&=tot[j]&1;
if(vis[num[i]/j]) flag&=tot[num[i]/j]&1;
}
}
if(flag){puts("1.000000000"); return 0;}
}
puts("0.000000000");
return 0;
}