PE 560 【博弈】【fwt】

本文解析了一道经典的取石子游戏问题,其中规定取走的石子数必须与当前石子总数互质。通过分析得出先手必败的情况,并采用倍增与快速沃尔特变换(FWT)的方法进行求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

跪着%yyy半年前秒掉的题

题目大意:经典的取石子游戏,但规定取走的石子数必须与当前这堆的石子总数互质。现在求堆数为107,每堆石子的数目为[1,107)中,有多少种先手必败的可能。

嗯……先打表……
发现如下规律

sg[n]=10ksg[ms[n]]n==1n==2nkotherwise

现在就是要求xor等于0的方案数
倍增,滋磁
设f[i,j]表示前2i个数异或为j的方案数
fwt转移

【答案】994345168

#include <iostream>
#include <cstdio>
#define LL long long
#define M 1000000007
using namespace std;

const int N = 1048576,n = 10000000;
int tot,inv2;
int sg[n + 10],prime[n + 10],v[n + 10];
LL f[24][N],ans[N];

LL ksm(LL a,int b)
{
    LL ret = 1;
    for (;b;b >>= 1,a = a * a % M)
        if (b & 1) ret = ret * a % M;
    return ret;
}

void get_prime()
{
    sg[1] = 1;
    for (int i = 2;i < n;i ++)
    {
        if (!v[i])
        {
            prime[tot ++] = i;
            if (i ^ 2) sg[i] = tot; 
        }
        for (int j = 0;j < tot && i * prime[j] < n;j ++)
        {
            v[i * prime[j]] = 1;
            sg[i * prime[j]] = sg[prime[j]];
            if (i % prime[j] == 0) break; 
        }
    }
}

void DWT(LL *a,int l,int r)
{
    if (l == r) return;
    int m = l+r >> 1;
    DWT(a,l,m),DWT(a,m+1,r);
    for (int i = 0;i <= m - l;i ++)
    {
        LL x = (a[i + l] + a[m + 1 + i]) % M,y = (a[i + l] - a[m + 1 + i]) % M;
        a[i + l] = x,a[m + 1 + i] = y;
    }
}

void IDWT(LL *a,int l,int r)
{
    if (l == r) return;
    int m = l+r >> 1;
    for (int i = 0;i <= m - l;i ++)
    {
        LL x = (a[i + l] + a[m + 1 + i]) % M * inv2 % M;
        LL y = (a[i + l] - a[m + 1 + i]) % M * inv2 % M;
        a[i + l] = x,a[m + 1 + i] = y;
    }
    IDWT(a,l,m),IDWT(a,m+1,r);
}

int main()
{
    get_prime(),inv2 = ksm(2,M-2);
    for (int i = 1;i < n;i ++) f[0][sg[i]] ++;
    DWT(f[0],0,N-1);
    for (int i = 1;i < 24;i ++)
        for (int j = 0;j < N;j ++) f[i][j] = f[i - 1][j] * f[i - 1][j] % M;
    ans[0] = 1,DWT(ans,0,N-1);
    for (int i = 0;i < 24;i ++) if (n & (1 << i))
        for (int j = 0;j < N;j ++) ans[j] = ans[j] * f[i][j] % M;
    IDWT(ans,0,N-1);
    cout << (ans[0] + M) % M << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值