取石子 - 博弈论 - Nim游戏

题目大意:有n堆石子,第i堆有ai个。每次可以选择一个x,选择一个质数p满足p|x,选择一个0< y<=a(x),然后将y个石子从x移动到x/p。问先手有多少种第一步移动的测率能够必胜?答案除以总方案数输出。 n 10 6 , 0 a i 10 9
题解:
从最简单的情况推导,什么情况下是必胜/必败。
只有质数位置上有值那么这就是最朴素的Nim游戏。
考虑如果只有p^0,p^1,…,p^k有值,那么这就是阶梯Nim游戏。
考虑用阶梯Nim游戏的正确性证明阐述这个问题(朴素的阶梯Nim游戏是条链,但是稍作推广就能发现这也适用于这个二分DAG。),发现几乎是相同的,那么结论就是所有指数之和为奇数的a的异或和s=0,则为必败。考虑这个题的计数。
首先如果s=0那么先手必败无论如何都赢不了直接输出0走人。
否则枚举所以指数之和为奇数的x,看能否将其变成y< x,使得(s^x)^y=0,也就是是否(s^x) < y,这样就做完了……是不可能的。
因此如果当前s> 0,正常的先手的确不会闲着没事去动偶数层的东西(有可能会从必胜态变为必胜),而每次都是后手动了这些东西先收才会动。但是这个题里面还是有可能先手傻了吧唧的动了偶数层又歪打正着的变成了先手必败,要考虑这种情况,和上一种情况类似。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define lint long long
#define mod 998244353
#define N 1000010
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int p[N],c[N],d[N],a[N],cnt,np[N];
inline int fast_pow(int x,int k,int ans=1)
{ for(;k;k>>=1,x=(lint)x*x%mod) (k&1)?ans=(lint)ans*x%mod:0;return ans; }
inline int prelude(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!np[i]) p[++cnt]=i,c[i]=d[i]=1;
        for(int j=1;j<=cnt&&p[j]<=n/i;j++)
        {
            int x=p[j]*i;np[x]=1,c[x]=c[i]^1;
            if(i%p[j]) d[x]=d[i]+1;
            else { d[x]=d[i];break; }
        }
    }
    return 0;
}
int main()
{
    int n=inn(),s=0,ans=0,tot=0;prelude(n);
    for(int i=1;i<=n;i++) s^=c[i]*(a[i]=inn());
    if(!s) return !printf("0\n");
    for(int i=1;i<=n;i++) tot=(tot+(lint)d[i]*a[i])%mod;
    for(int i=1,t;i<=n;i++) if(c[i])
        if((t=s^a[i])<a[i]) ans+=d[i];
        else for(int j=1;j<=cnt&&p[j]<=n/i;j++) ans+=(t-a[i]<=a[p[j]*i]);
    return !printf("%lld\n",(lint)ans%mod*fast_pow(tot,mod-2)%mod);
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/82384980