这是我第一道独立做出来的FWT的题目,所以写篇随笔纪念一下。

(这还要纪念,我太弱了)

题目链接:

BZOJ

题目大意:两人玩nim游戏(多堆石子,每次可以从其中一堆取任意多个,不能操作就输)。$T$ 组数据,现在问如果 $n$ 堆石子,每堆石子个数都是不超过 $m$ 的素数,有多少种不同的石子序列使得先手没有必胜策略,答案对 $10^9+7$ 取模。(石子堆顺序不同也算不同)

$1\leq T\leq 80,1\leq n\leq 10^9,1\leq m\leq 5\times 10^4$。


首先肯定要把 $m$ 以内的素数筛出来。

nim游戏的SG函数大家都知道吧!就是它本身。

所以若先手没有必胜策略,那么所有石子堆的石子个数的异或和为 $0$。

考虑 $dp[i][j]$ 为前 $i$ 堆石子,异或和为 $j$ 的石子序列总数。题目要求即为 $dp[n][0]$。

另外令 $g[x]=[x\leq m \&\& x\in prime]$,即若 $x$ 为 $m$ 以内的素数则 $g[x]=1$ 否则 $g[x]=0$。

(以下 $\oplus$ 均代表异或)

那么有边界:

$dp[1][x]=[g[x]==1]$

考虑到 $j\oplus k\oplus k=j$,那么转移方程:

$dp[i][j]=\sum^m_{k=1}dp[i-1][j\oplus k]\quad [g[k]==1]$

这样暴力转移复杂度是 $O(Tm^2n)$ 的,考虑优化。

可以发现 $g[x]=[g[x]==1]$,那么边界和转移方程可以化为:

$dp[1][x]=g[x]$

$dp[i][j]=\sum^m_{k=1}dp[i-1][j\oplus k]g[k]$

发现这其实是个多项式异或卷积的形式(因为 $dp[i-1][j\oplus k]g[k]$ 会贡献到 $dp[i][j]=dp[i][(j\oplus k)\oplus k]$)

那么用 $FWT$ 优化转移。时间复杂度优化至 $O(Tm(\log m)n)$。但还是不够!

我们发现:

$dp[1][x]=g[x]$,也就是 $dp[1]$ 是 $g$ 本身,即异或卷积意义下的 $g^1$

$dp[2][x]=\sum^m_{k=1}dp[1][x\oplus k]g[k]=\sum^m_{k=1}g[x\oplus k]g[k]$,也就是 $dp[2]$ 是异或卷积意义下的 $g^2$

$\dots\dots$

至于 $dp[3]$ 我推不出来

我们科学证明一下:异或卷积是满足结合律的,所以若 $dp[i-1]$ 是 $g^{i-1}$,那么 $dp[i]$ 就是 $dp[i-1]\times g=g^{i-1}\times g=g^i$。

所以 $dp[i]=g^i$。

刚刚说了异或卷积满足结合律,所以可以快速幂加速求 $dp[n]=g^n$,那么 $dp[n][0]$ 也就求完了,问题迎刃而解。

时间复杂度 $O(Tm\log m\log n)$,还差一点点才能通过。

加一个小优化就行了:在快速幂中乘法要乘很多次,如果每乘完一次就要 $O(mlogm)$ 变换就浪费了。可以一开始 $FWT$,快速幂的过程中只有点值相乘没有变换,完了之后再 $FWT$,少了很多运算。

这样就可以用 $O(Tm(\log m+\log n))$ 通过了。

(p.s:$FWT$ 是对模数没有要求的,不要被 $10^9+7$ 吓到了。)


上代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=,inv=; //2的逆元为500000004
int n,m,limit;
int a[],ans[];
int prime[],pl;
bool vis[];
void FWT(int *c,int type){ //模板
for(int mid=;mid<limit;mid<<=)
for(int r=mid<<,j=;j<limit;j+=r)
for(int k=;k<mid;k++){
int x=c[j+k],y=c[j+k+mid];
c[j+k]=(x+y)%mod;c[j+k+mid]=(x-y+mod)%mod;
if(type==-){
c[j+k]=1ll*c[j+k]*inv%mod;c[j+k+mid]=1ll*c[j+k+mid]*inv%mod;
}
}
}
void mult(int *a,int *b,int *c){ //点值相乘(为何这里面没有FWT?上面说过这会浪费时间)
for(int i=;i<limit;i++) c[i]=1ll*a[i]*b[i]%mod;
}
void quickpow(int *a,int b){
memset(ans,,sizeof(ans));
ans[]=; //初始化
FWT(a,);FWT(ans,); //一开始就变换
while(b){ //快速幂
if(b&) mult(ans,a,ans);
mult(a,a,a);
b>>=;
} //中间不变换
FWT(a,-);FWT(ans,-); //这时才变换回去
}
void init(){ //筛素数,作为转移数组
vis[]=true;
for(int i=;i<=;i++){
if(!vis[i]) prime[++pl]=i;
for(int j=;j<=pl && i*prime[j]<=;j++){
vis[i*prime[j]]=true;
if(i%prime[j]==) break;
}
}
}
int main(){
init();
while(~scanf("%d%d",&n,&m)){
memset(a,,sizeof(a));
for(limit=;limit<=m;limit<<=);
for(int i=;i<=pl && prime[i]<=m;i++) a[prime[i]]=; //转移数组
quickpow(a,n);
printf("%d\n",ans[]);
}
}

FWT

BZOJ4589 Hard Nim(快速沃尔什变换FWT)的更多相关文章

  1. 一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记

    一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记 曾经某个下午我以为我会了FWT,结果现在一丁点也想不起来了--看来"学"完新东西不经常做题不写博客,就白学了 = = 我没啥智 ...

  2. 快速沃尔什变换FWT

    快速沃尔什变换\(FWT\) 是一种可以快速完成集合卷积的算法. 什么是集合卷积啊? 集合卷积就是在集合运算下的卷积.比如一般而言我们算的卷积都是\(C_i=\sum_{j+k=i}A_j*B_k\) ...

  3. 集合并卷积的三种求法(分治乘法,快速莫比乌斯变换(FMT),快速沃尔什变换(FWT))

    也许更好的阅读体验 本文主要内容是对武汉市第二中学吕凯风同学的论文<集合幂级数的性质与应用及其快速算法>的理解 定义 集合幂级数 为了更方便的研究集合的卷积,引入集合幂级数的概念 集合幂级 ...

  4. 【学习笔鸡】快速沃尔什变换FWT

    [学习笔鸡]快速沃尔什变换FWT OR的FWT 快速解决: \[ C[i]=\sum_{j|k=i} A[j]B[k] \] FWT使得我们 \[ FWT(C)=FWT(A)*FWT(B) \] 其中 ...

  5. 关于快速沃尔什变换(FWT)的一点学习和思考

    最近在学FWT,抽点时间出来把这个算法总结一下. 快速沃尔什变换(Fast Walsh-Hadamard Transform),简称FWT.是快速完成集合卷积运算的一种算法. 主要功能是求:,其中为集 ...

  6. 快速沃尔什变换 FWT 学习笔记【多项式】

    〇.前言 之前看到异或就担心是 FWT,然后才开始想别的. 这次学了 FWT 以后,以后判断应该就很快了吧? 参考资料 FWT 详解 知识点 by neither_nor 集训队论文 2015 集合幂 ...

  7. Codeforces 662C(快速沃尔什变换 FWT)

    感觉快速沃尔什变换和快速傅里叶变换有很大的区别啊orz 不是很明白为什么位运算也可以叫做卷积(或许不应该叫卷积吧) 我是看 http://blog.csdn.net/liangzhaoyang1/ar ...

  8. BZOJ4589 Hard Nim(博弈+FWT)

    即使n个数的异或为0.如果只有两堆,将质数筛出来设为1,做一个异或卷积即可.显然这个东西满足结合律,多堆时直接快速幂.可以在点值表示下进行. #include<iostream> #inc ...

  9. bzoj千题计划308:bzoj4589: Hard Nim(倍增FWT+生成函数)

    https://www.lydsy.com/JudgeOnline/problem.php?id=4589 n*m*m 做法 dp[i][j] 前i堆石子,异或和为j的方案数 第一重循环可以矩阵快速幂 ...

随机推荐

  1. CS50.3

    1,int()取整函数 2,RPG(role playing game )角色扮演游戏 3,代码写了,要跑,需要compiler (编译器) 4,CLI(command-line interface) ...

  2. [LOJ#6066]. 「2017 山东一轮集训 Day3」第二题[二分+括号序列+hash]

    题意 题目链接 分析 首先二分,假设二分的答案为 \(mid\),然后考虑利用括号序列来表示树的形态. 点 \(u\) 的 \(k-\) 子树的括号序列表示实际上是刨去了 \(u\) 子树内若干个与 ...

  3. Java虚拟机笔记(五):JVM中对象的分代

    为什么要分代 为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能.你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我们要找到哪些对象没用, ...

  4. Redis数据备份、安全、管理服务器笔记

    Redis 数据备份与恢复 Redis SAVE 命令用于创建当前数据库的备份. 实例 redis > SAVE OK 恢复数据 如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 ...

  5. VS 远程调试 Azure Web App

    如果能够远程调试部署在 Azure 上的 Web App,将会极大的提高我们修复 bug 的效率.Visual Studio 一贯以功能强大.好用著称,当然可以通吃基于 Azure 应用的创建.发布和 ...

  6. 《杜增强讲Unity之Tanks坦克大战》10-相机控制

    10 相机控制 本节主要学习如何控制相机自动调整位置和焦距,使两个坦克一直同时在视野内.   image 在Hierarchy点击右键   image 点击 Create Empty,生成空对象,改名 ...

  7. 通过sqli-labs学习sql注入——基础挑战之less1

    环境准备: Phpstudy  (PHP+Apache+Mysql) Sql-lab 首先了解下基础知识: URL编码: 因为在浏览器中,当我们访问一个网址的时候,浏览器会自动将用户输入的网址进行UR ...

  8. thinkphp在wamp 配置去掉url中index.php方法

    http://blog.csdn.net/youmypig/article/details/45008971

  9. 【SE】Week3 : 四则运算式生成评分工具Extension&Release Version(结对项目)

    Foreword 此次的结对项目终于告一段落,除了本身对软件开发的整体流程有了更深刻的了解外,更深刻的认识应该是结对编程对这一过程的促进作用. 在此想形式性但真心地啰嗦几句,十分感谢能端同学能够不厌其 ...

  10. 《Linux内核设计与实现》 第三周 读书笔记

    第一章 Linux内核简介 1. Unix的历史 Unⅸ虽然已经使用了40年,但计算机科学家仍然认为它是现存操作系统中最强大和最优秀的系统. Unix强大的根本原因: 简洁 在Unix中所有的东西都被 ...