bzoj 4589: Hard Nim

传送门

先筛出m以内的质数,g[i]表示i是否是素数

f[i][j]表示前n堆数异或和为j的方案数。

f[0][0]=1; f[0][1]~f[0][m]=0;

f[i][j] = sigma( f[i-1][k] * g[k^j] )

发现这个玩意满足乘法结合律

∴ f[n][] = sigma(f[0][]*g[]*g[]*g[]*g[]……)

=sigma(f[0]*g[]^n) ;

f[0][]为单位元

所以ans=g[]^n

g[]^n可以用FWT+快速幂来算,时间复杂度就是log^2的

然后发现每次FWT变换,相乘,答案变回来,再乘的时候再FWT变换,变回来。。这些步骤是不必要的。

直接FWT然后快速幂,再变换回去就好看,时间复杂度为一个log

(以上感谢llj同学。)

然后就是一个愉快的FWT模板

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<ctime>
const int N=100007,M=50000,mod=1e9+7;
typedef long long LL;
using namespace std;
int n,m,l,bo[M+5],p[M+5];
LL g[N],inv;

template<typename T> void read(T &x) {
    T f=1; x=0; char ch=getchar();
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
    if(ch==‘-‘) f=-1,ch=getchar();
    for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; x*=f;
}

void get_prime() {
    for(int i=2;i<=M;i++) {
        if(!bo[i]) p[++p[0]]=i;
        for(int j=1;j<=p[0]&&p[j]*i<=M;j++) {
            bo[p[j]*i]=1;
            if(i%p[j]==0) break;
        }
    }
}

LL ksm(LL a,LL b) {
    LL base=a,res=1;
    while(b) {
        if(b&1) res=res*base%mod;
        base=base*base%mod;
        b>>=1;
    }
    return res;
}

void FWT(LL a[],int f) {
    for(int i=1;i<n;i<<=1)
        for(int q=i<<1,j=0;j<n;j+=q)
            for(int k=0;k<i;k++) {
                LL x=a[j+k],y=a[j+k+i];
                 if(f==1) {
                     a[j+k]=(x+y)%mod; a[j+k+i]=(x-y+mod)%mod;//^
                     //& a[j+k]=x+y;
                     //| a[j+k+i]=x+y;
                 }
                 else {
                     a[j+k]=(x+y)%mod*inv%mod; a[j+k+i]=(x-y+mod)%mod*inv%mod;//^
                     //& a[j+k]=x-y;
                     //| a[j+k+i]=y-x;
                 }
            }
}

int main() {
    get_prime(); bo[1]=1; bo[0]=1;
    inv=ksm(2,mod-2);
    while(scanf("%d",&n)!=EOF) {
        read(m); int nn=n;
        memset(g,0,sizeof(g));
        for(int i=0;i<=m;i++) g[i]=(bo[i]==0);
        l=0;
        for(n=1;n<=m;n<<=1) l++;
        FWT(g,1);
        for(int i=0;i<n;i++) g[i]=ksm(g[i],nn);
        FWT(g,-1);
        printf("%lld\n",g[0]);
    }
    return 0;
}
/*
3 7
4 13
*/

原文地址:https://www.cnblogs.com/Achenchen/p/8312914.html

时间: 2024-11-14 13:02:53

bzoj 4589: Hard Nim的相关文章

bzoj 4589 Hard Nim —— FWT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4589 先手必败,是一开始所有石子的异或和为0: 生成函数 (xpri[1] + xpri[2] + ... + xpri[k])n,pri[k] <= m FWT求解即可: 而且不要快速幂里面每次变换来变换去的,只有快速幂前后需要变换. 代码如下: #include<iostream> #include<cstdio> #include<cstring> #

bzoj 4589 Hard Nim——FWT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4589 一开始异或和为0的话先手必败.有 n 堆,每堆可以填那些数,求最后异或和为0的方案数,就是一个快速幂的异或FWT. 注意快速幂的过程中对那些数组直接乘就行,不用总是FWT!!! 为什么比Zinn慢了1008ms? #include<iostream> #include<cstdio> #include<cstring> #include<algorit

【BZOJ 2819】 Nim

2819: Nim Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 926  Solved: 355 [Submit][Status] Description 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.于是vfleaking决定写一个玩Nim游戏的平台来坑玩家. 为了设计漂亮一点的初始局面,vfl

BZOJ 3105 新Nim游戏

注意到线性基的非空子集的异或都不是0. 我们的目的就是消出这样一个线性基,是对面再怎么拿,异或和都是1. 从大到小排序消就好了 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 105 using namespace std; long long n,a[maxn],b[maxn],bit[32],ans=0,sum=0; bool v

【BZOJ】【2819】NIM

这题……咋说捏,其实是一道披着博弈论外衣的树上操作问题…… 随便用dfs序或者树链剖分转成序列,然后查询路径上的所有点的NIM和(异或和)就行了,毕竟除了是在树上以外,就是裸的NIM问题. 树链剖分:一开始把线段树写跪了,然后输出“Yes”和“No”的时候全部大写了,再然后发现线段树空间开小了…… 代码如下: 1 //BZOJ 2819 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 #includ

【BZOJ】4147: [AMPPZ2014]Euclidean Nim

[算法]博弈论+数论 [题意]给定n个石子,两人轮流操作,规则如下: 轮到先手操作时:若石子数<p添加p个石子,否则拿走p的倍数个石子 : 轮到后手操作时:若石子数<q添加q个石子,否则拿走q的倍数个石子 . 拿走所有石子的人胜利,问先手是否必胜,或输出游戏会永远进行下去 [题解]引用自:BZOJ 4147 AMPPZ2014 Euclidean Nim 博弈论+数论 by popoqqq 施工中--

BZOJ 1874 取石子游戏 (NIM游戏)

题解:简单的NIM游戏,直接计算SG函数,至于找先手策略则按字典序异或掉,去除石子后再异或判断,若可行则直接输出. #include const int N=1005; int SG[N],b[N],hash[N],a[N],sum,tmp,i,j,n,m; void FSG(int s){ SG[0]=0; for(int i=1;i<=s;i++){ for(int j=1;b[j]<=i&&j<=m;j++)hash[SG[i-b[j]]]=i; for(int j

BZOJ 3105:[cqoi2013]新Nim游戏

BZOJ 3105:[cqoi2013]新Nim游戏 题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3105 题目大意:在传统的Nim取石子游戏中做了改变:两人刚开始可以取走任意堆石子(不包括全部)后进行传统游戏,问先手能否必胜,若必胜求出刚开始最少取多少石子. 线性基 传统Nim游戏先手必胜的前提条件为$a_0 \lxor a_1 \lxor a_2 \lxor ... \lxor a_{n-1} \neq 0$. 故若欲使新Nim游

BZOJ 3105: [cqoi2013]新Nim游戏

3105: [cqoi2013]新Nim游戏 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1064  Solved: 624[Submit][Status][Discuss] Description 传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同).两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴.可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿.拿走最后一根火柴的游戏者胜利. 本题的游