纸牌游戏

题目描述

华华和秀秀在玩纸牌游戏,游戏的规则如下:

初始时,桌面上有n张纸牌,每张纸牌上写有一个正整数。游戏开始时华华先在黑板上写上数字0,之后秀秀和华华轮流选取纸牌(秀秀先手)。当一个人选定一张纸牌时,他需要将黑板上的数字改写成这个数和纸牌上的数的最大公约数,然后将这张纸牌丢弃。当一个人写下了1 或者无法选取纸牌时,他就输了。

现在秀秀想知道:

  1. 当华华和秀秀都按照随机策略选取卡片时,秀秀获胜的概率有多少;

  2. 当华华和秀秀都按照最优策略选取卡片时,秀秀获胜的概率有多少。

题解

两个问题是完全没关系的两道题。
对于前者,我们可以记取了 i i 张牌当前黑板上东西是 j j 的概率。
j j 没有变,说明你又取了一个 j j 的倍数则 f [ i ] [ j ] = f [ i 1 ] [ j ] ( s u m [ j ] i + 1 ) / ( n i + 1 ) f[i][j]=f[i-1][j]*(sum[j]-i+1)/(n-i+1)
j j 变成了 g c d ( j , a [ k ] ) gcd(j,a[k]) k k 枚举一下暴力转移就行了,写的时候主动转移更方便。统计答案如果下一次 j j 变成了 1 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;
}

猜你喜欢

转载自blog.csdn.net/qq_32461955/article/details/83548591