FWT可以解决位运算卷积问题。

即\(h(i)=\sum\limits_{j⊕k=i} f(j)*g(k)\),其中“⊕”表示位运算。

与卷积:

定义\(f\)到\(F\)的变换:\(F(i)=\sum\limits_{j\&i==i}^{ }f(j)\)。

这样,若\(h(i)=\sum\limits_{j and k=i} f(j)*g(k)\),则\(H(i)=F(i)*G(i)\)。

变换方法:就是按照长度为\(2^i\)分段,把每段的后半部分加到前半部分(1对0有额外贡献)。

逆变换就是减回去。时间复杂度:\(O(nlogn)\)。

代码:

void fwtand(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+k]=(sz[j+k]+sz[j+(i>>1)+k])%md;
}
}
}
void ifwtand(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+k]=(sz[j+k]-sz[j+(i>>1)+k]+md)%md;
}
}
}

或卷积:

与“与卷积”类似。

定义\(f\)到\(F\)的变换:\(F(i)=\sum\limits_{j|i==i}^{ }f(j)\)。

这样,若\(h(i)=\sum\limits_{j or k=i} f(j)*g(k)\),则\(H(i)=F(i)*G(i)\)。

变换方法:就是按照长度为\(2^i\)分段,把每段的前半部分加到后半部分(0对1有额外贡献)。

逆变换就是减回去。时间复杂度:\(O(nlogn)\)。

代码:

void fwtor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+(i>>1)+k]=(sz[j+(i>>1)+k]+sz[j+k])%md;
}
}
}
void ifwtor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+(i>>1)+k]=(sz[j+(i>>1)+k]-sz[j+k]+md)%md;
}
}
}

这两个其实是高维前/后缀和

异或卷积:

这个比较常用。

定义\(f\)到\(F\)的变换:\(F(i)=\sum\limits_{j=0}^{2^n-1}(-1)^{bit(j and i)}f(j)\)。

这样,若\(h(i)=\sum\limits_{j xor k=i} f(j)*g(k)\),则\(H(i)=F(i)*G(i)\)。

变换方法:就是按照长度为\(2^i\)分段,把每段的前半部分变为前半部分加后半部分,

后半部分变为前半部分减后半部分。

逆变换就是相当于已知\(a+b=x,a-b=y\),则\(a=(x+y)/2,b=(x-y)/2\)。

就是正变换再除以2。

时间复杂度:\(O(nlogn)\)。

代码:

void fwtxor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
{
int a=sz[j+k],b=sz[j+(i>>1)+k];
sz[j+k]=(a+b)%md;
sz[j+(i>>1)+k]=(a-b+md)%md;
}
}
}
}
void ifwtxor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
{
int a=sz[j+k],b=sz[j+(i>>1)+k];
sz[j+k]=1ll*(a+b)*inv%md;
sz[j+(i>>1)+k]=1ll*(a-b+md)*inv%md;
}
}
}
}

FST:子集卷积

即\(h(i)=\sum\limits_{j or k=i且j and k=0} f(j)*g(k)\)。

比或卷积多了一个限制。

我们发现,设\(s(i)\)表示\(i\)的二进制表示中1的个数,那么如果\(i\|j=k,i\&j=0\),则\(s(i)+s(j)=s(k)\)。

利用这个性质,我们可以加一维表示\(s\),在\(F*G\)时考虑\(s\)的限制。

时间复杂度:\(O(nlog^2n)\)。

代码:

for(int i=0;i<len;i++)
{
for(int j=0;j<17;j++)
{
if(i&(1<<j))
sl[i]+=1;
}
}
for(int i=0;i<len;i++)
a[sl[i]][i]=sz[i];
for(int i=0;i<18;i++)
fwtor(a[i],len);
for(int i=0;i<18;i++)
{
for(int j=0;i+j<18;j++)
{
for(int k=0;k<len;k++)
h1[i+j][k]=(h1[i+j][k]+1ll*a[i][k]*a[j][k])%md;
}
}
for(int i=0;i<18;i++)
ifwtor(h1[i],len);
for(int i=0;i<len;i++)
ab[i]=h1[sl[i]][i];

例题:

CF914G

题意:

给你一个长度为\(n\)的数组\(s\).定义五元组\((a,b,c,d,e)\)是合法的当且仅当:

①.\(1\le a,b,c,d,e\le n\)

②.\((s_a|s_b)\&s_c\&(s_d\)^\(s_e)=2^i,i\in Z\)

③.\(s_a\&s_b=0\)

对于所有合法的五元组\((a,b,c,d,e)\)

求\(\sum f(s_a|s_b)*f(s_c)*f(s_d\)^\(s_e)\mod 10^9+7\)

\(f_0=0,f_1=1,f_i=f_{i-1}+f_{i-2}\)

\(1\le n\le10^6,0\le s_i\lt2^{17}\)

模板题。

先考虑\((s_a|s_b)\),发现是FST卷积。

再考虑\((s_d\)^\(s_e)\),是异或卷积。

然后就是与卷积了。

代码:

#include <stdio.h>
#define md 1000000007
#define inv 500000004
#define len 131072
int sz[132000],sl[132000];
void fwtor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+(i>>1)+k]=(sz[j+(i>>1)+k]+sz[j+k])%md;
}
}
}
void ifwtor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+(i>>1)+k]=(sz[j+(i>>1)+k]-sz[j+k]+md)%md;
}
}
}
void fwtand(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+k]=(sz[j+k]+sz[j+(i>>1)+k])%md;
}
}
}
void ifwtand(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
sz[j+k]=(sz[j+k]-sz[j+(i>>1)+k]+md)%md;
}
}
}
void fwtxor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
{
int a=sz[j+k],b=sz[j+(i>>1)+k];
sz[j+k]=(a+b)%md;
sz[j+(i>>1)+k]=(a-b+md)%md;
}
}
}
}
void ifwtxor(int sz[132000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
for(int j=0;j<n;j+=i)
{
for(int k=0;k<(i>>1);k++)
{
int a=sz[j+k],b=sz[j+(i>>1)+k];
sz[j+k]=1ll*(a+b)*inv%md;
sz[j+(i>>1)+k]=1ll*(a-b+md)*inv%md;
}
}
}
}
int a[18][132000],h1[18][132000],fib[132000],h2[132000];
int ab[132000],de[132000],ans[132000];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int a;
scanf("%d",&a);
sz[a]+=1;
}
for(int i=0;i<len;i++)
{
for(int j=0;j<17;j++)
{
if(i&(1<<j))
sl[i]+=1;
}
}
fib[0]=0;fib[1]=1;
for(int i=2;i<len;i++)
fib[i]=(fib[i-1]+fib[i-2])%md;
for(int i=0;i<len;i++)
a[sl[i]][i]=sz[i];
for(int i=0;i<18;i++)
fwtor(a[i],len);
for(int i=0;i<18;i++)
{
for(int j=0;i+j<18;j++)
{
for(int k=0;k<len;k++)
h1[i+j][k]=(h1[i+j][k]+1ll*a[i][k]*a[j][k])%md;
}
}
for(int i=0;i<18;i++)
ifwtor(h1[i],len);
for(int i=0;i<len;i++)
ab[i]=h1[sl[i]][i];
for(int i=0;i<len;i++)
de[i]=sz[i];
fwtxor(de,len);
for(int i=0;i<len;i++)
de[i]=1ll*de[i]*de[i]%md;
ifwtxor(de,len);
for(int i=0;i<len;i++)
{
ab[i]=1ll*ab[i]*fib[i]%md;
sz[i]=1ll*sz[i]*fib[i]%md;
de[i]=1ll*de[i]*fib[i]%md;
}
fwtand(ab,len);
fwtand(sz,len);
fwtand(de,len);
for(int i=0;i<len;i++)
ans[i]=1ll*ab[i]*sz[i]%md*de[i]%md;
ifwtand(ans,len);
int jg=0;
for(int i=1;i<=len;i=(i<<1))
jg=(jg+ans[i])%md;
printf("%d",jg);
return 0;
}

uoj310【UNR #2】黎明前的巧克力

题意:有一个集合,选出两个不相交的子集,使其异或和相等,问方案数。

考虑dp:设\(dp(i,j)\)表示考虑到i,两人异或为j的方案数。

则\(dp(i,j)=dp(i-1,j)+2*dp(i-1,j\)^\(a(i))\)。

考虑FWT:对每个i构造A,使\(A(0)=1,A(a(i))=2\)。

对每个A做FWT,乘起来后再IFWT。但是复杂度太高。

根据公式,可以发现,FWT(A)的每位只能是3或-1。

那么,只要知道FWT后,A的每个对应位置之和,就能解出3和-1的数量了,之后快速幂即可。

根据加法的运算律,可以得知若干个长度相等的序列FWT后对应位置求和,等于先求和,再FWT。

所以求和后,FWT,之后快速幂算出每个位置的值,再IFWT,最后位置0的值减1就是答案。

时间复杂度:\(O(mlogm)\)。

思路非常巧妙。

代码:

#include <stdio.h>
#define md 998244353
#define inv 499122177
#define len 1048576
void fwt(int sz[1050000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
int t=(i>>1);
for(int j=0;j<n;j+=i)
{
for(int k=j;k<j+t;k++)
{
int a=sz[k+t];
sz[k+t]=sz[k]-a;
sz[k]=sz[k]+a;
}
}
}
}
void ifwt(int sz[1050000],int n)
{
for(int i=2;i<=n;i=(i<<1))
{
int t=(i>>1);
for(int j=0;j<n;j+=i)
{
for(int k=j;k<j+t;k++)
{
int a=sz[k+t];
sz[k+t]=1ll*(sz[k]-a+md)*inv%md;
sz[k]=1ll*(sz[k]+a)*inv%md;
}
}
}
}
int sz[1050010],mi[1050000],m3[1050010],sl[1050000];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&sz[i]);
sl[0]=n;
for(int i=0;i<n;i++)
sl[sz[i]]+=2;
fwt(sl,len);
for(int i=0;i<len;i++)
mi[i]=(n+sl[i])/4;
m3[0]=1;
for(int i=1;i<=n;i++)
m3[i]=3ll*m3[i-1]%md;
for(int i=0;i<len;i++)
{
if((n-mi[i])%2==0)
mi[i]=m3[mi[i]];
else
mi[i]=md-m3[mi[i]];
}
ifwt(mi,len);
printf("%d",(mi[0]-1+md)%md);
return 0;
}

扩展

这个技巧还可以扩展:

就是说当权值有k个时,我们先将其中一个权值变为0,这样总共可能的贡献有\(2^{k-1}\)种。(每个系数是1或-1),0的贡献一定是1。

为了把这\(2^{k-1}\)种的数量分别求出来,我们需要找\(2^{k-1}\)个等式。

可以枚举剩余k-1个元素的子集,只将其异或的位置+1,做FWT。

这样,每个位置,会得到\(2^{k-1}\)个数。将这些数做FWT后,就可以的得到\(2^{k-1}\)种可能分别的数量,快速幂即可。

证明略。

(核心)代码:

int xo=0,mi=1;
for(int i=0;i<n;i++)
{
for(int j=0;j<k;j++)
scanf("%d",&p[i][j]);
xo^=p[i][0];
for(int j=1;j<k;j++)
p[i][j]^=p[i][0];
}
ans[xo]=1;
fwtxor(ans,(1<<m));
for(int s=0;s<(1<<(k-1));s++)
{
for(int i=0;i<n;i++)
{
int z=0;
for(int j=1;j<k;j++)
{
if(s&(1<<(j-1)))
z^=p[i][j];
} sz[z]+=1;
}
fwtxor(sz,(1<<m));
for(int i=0;i<(1<<m);i++)
{ nf[(i<<(k-1))|s]=sz[i];
sz[i]=0;
}
}
for(int i=0;i<(1<<m);i++)
{
for(int s=0;s<(1<<(k-1));s++)
zz[s]=nf[(i<<(k-1))|s];
ifwtxor(zz,1<<(k-1));
for(int s=0;s<(1<<(k-1));s++)
{
int he=sl[0];
for(int j=1;j<k;j++)
{
if(s&(1<<(j-1)))
he=(he-sl[j]+md)%md;
else
he=(he+sl[j])%md;
}
ans[i]=1ll*ans[i]*ksm(he,zz[s])%md;
}
}
ifwtxor(ans,(1<<m));

CF662C Binary Table

题意:

有一个 n 行 m 列的表格,每个元素都是 0/1 ,每次操作可以选择一行或一列,把 0/1 翻转,即把 0 换为 1 ,把 1 换为 0 。请问经过若干次操作后,表格中最少有多少个 1。\((1\leq n \leq 20,1\leq m \leq 10^5)\)。

首先,我们可以枚举行的交换,共\(2^n\)种。

然后,对每一列,考虑它是否交换。复杂度为\(O(nm2^n)\)。

考虑优化:

首先,我们发现,如果记翻转为1,那么翻转就是异或。

记B数组表示状态压缩后的每列的出现次数。

记A数组表示一列为这个状态的1的最少个数。

那么,设\(C_i=A_{ixorj}*B_j\)之和,那么C的最小值就是答案。

反一下,将A和B做异或卷积,即可得到C。时间复杂度\(O(nm+n2^n)\)。

代码:

#include <stdio.h>
#define ll long long
void fwt(ll sz[1048576],int n)
{
for(int h=2;h<=n;h=(h<<1))
{
for(int i=0;i<n;i+=h)
{
for(int j=0;j<(h>>1);j++)
{
ll a=sz[i+j],b=sz[i+j+(h>>1)];
sz[i+j]=a+b;
sz[i+j+(h>>1)]=a-b;
}
}
}
}
void ifwt(ll sz[1048576],int n)
{
for(int h=2;h<=n;h=(h<<1))
{
for(int i=0;i<n;i+=h)
{
for(int j=0;j<(h>>1);j++)
{
ll a=sz[i+j],b=sz[i+j+(h>>1)];
sz[i+j]=(a+b)/2;
sz[i+j+(h>>1)]=(a-b)/2;
}
}
}
}
int sz[20][100005];char zf[100005];
ll sa[1048576],sb[1048576];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%s",zf);
for(int j=0;j<m;j++)
sz[i][j]=zf[j]-'0';
}
for(int i=0;i<(1<<n);i++)
{
int s=0;
for(int j=0;j<n;j++)
{
if(i&(1<<j))
s+=1;
}
sa[i]=n-s;
if(s<sa[i])sa[i]=s;
}
for(int i=0;i<m;i++)
{
int s=0;
for(int j=0;j<n;j++)
{
if(sz[j][i])
s|=(1<<j);
}
sb[s]+=1;
}
fwt(sa,1<<n);fwt(sb,1<<n);
for(int i=0;i<(1<<n);i++)
sa[i]*=sb[i];
ifwt(sa,1<<n);
int ans=99999999;
for(int i=0;i<(1<<n);i++)
{
if(sa[i]<ans)
ans=sa[i];
}
printf("%d",ans);
return 0;
}

FWT 等总结 题解的更多相关文章

  1. $FFT/NTT/FWT$题单&简要题解

    打算写一个多项式总结. 虽然自己菜得太真实了. 好像四级标题太小了,下次写博客的时候再考虑一下. 模板 \(FFT\)模板 #include <iostream> #include < ...

  2. 【题解】#6622. 「THUPC 2019」找树 / findtree(Matrix Tree+FWT)

    [题解]#6622. 「THUPC 2019」找树 / findtree(Matrix Tree+FWT) 之前做这道题不理解,有一点走火入魔了,甚至想要一本近世代数来看,然后通过人类智慧思考后发现, ...

  3. 【题解】毒蛇越狱(FWT+容斥)

    [题解]毒蛇越狱(FWT+容斥) 问了一下大家咋做也没听懂,按兵不动没去看题解,虽然已经晓得复杂度了....最后感觉也不难 用FWT_OR和FWT_AND做一半分别求出超集和和子集和,然后 枚举问号是 ...

  4. 福建工程学院第十四届ACM校赛M题题解 fwt进阶,手推三进制fwt

    第九集,结束亦是开始 题意: 大致意思就是给你n个3进制的数字,让你计算有多少对数字的哈夫曼距离等于i(0<=i<=2^m) 思路: 这个是一个防ak题,做法是要手推公式的fwt 大概就这 ...

  5. CROC 2016 - Final Round [Private, For Onsite Finalists Only] C. Binary Table FWT

    C. Binary Table 题目连接: http://codeforces.com/problemset/problem/662/C Description You are given a tab ...

  6. 卷积FFT、NTT、FWT

    先简短几句话说说FFT.... 多项式可用系数和点值表示,n个点可确定一个次数小于n的多项式. 多项式乘积为 f(x)*g(x),显然若已知f(x), g(x)的点值,O(n)可求得多项式乘积的点值. ...

  7. HDU 5823 color II(FWT)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5823 [题目大意] 定义一张无向图的价值:给每个节点染色使得每条边连接的两个节点颜色不相同的最少颜 ...

  8. SDOI2017 Round2 详细题解

    这套题实在是太神仙了..做了我好久...好多题都是去搜题解才会的 TAT. 剩的那道题先咕着,如果省选没有退役就来填吧. 「SDOI2017」龙与地下城 题意 丢 \(Y\) 次骰子,骰子有 \(X\ ...

  9. UOJ#310.【UNR #2】黎明前的巧克力(FWT)

    题意 给出 \(n\) 个数 \(\{a_1, \cdots, a_n\}\),从中选出两个互不相交的集合(不能都为空),使得第一个集合与第二个集合内的数的异或和相等,求总方案数 \(\bmod 99 ...

随机推荐

  1. 程序计数器(关于java虚拟机内存的那些事)

    <深入理解java虚拟机> 读书感悟 作者:淮左白衣 --------------写于2018年4月9日17:44:48 关于java虚拟机内存的那些事之程序计数器 关于java虚拟机内存 ...

  2. 第十六章:网络IPC 套接字

    一.IP地址和端口 套接字接口可以用于计算机间通信.目前计算机间使用套接字通讯需要保证处于同一网段. 为了查看是否处于同一网段,我们可以使用IP地址判断. IP地址是计算机在网络中的唯一标识.IP地址 ...

  3. Spring @Transactional注解在什么情况下会失效,为什么?

    出处:  https://www.cnblogs.com/hunrry/p/9183209.html   https://www.cnblogs.com/protected/p/6652188.htm ...

  4. 01背包变种 第k解问题 hdu 2639

    先说说普通01包的状态问题吧 普通的01背包,在状态转移的过程中为了求出最优解,一定是遍历了所有的情况 然后再求的最优解.那么对于第k最优解问题,我们只需要再加一个维度,用来记录每一个状态k优解的状态 ...

  5. 谈jdbcTemplate与mybatis

    为什么会产生 Hibernate Mybatis 这类的dao层框架 传统的jdbc 虽然执行速度很快,但是开发效率很低,随着面向对象开发的设计思想,在面向对象编程中 将对象 进行持久化,存入关系型的 ...

  6. (一)easyUI之树形网络

    树形网格(TreeGrid)可以展示有限空间上带有多列和复杂数据电子表 一.案例一:按tree的数据结构来生成 前台 <%@ page language="java" con ...

  7. (一)Spring框架基础

    一.什么是spring框架 spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,i ...

  8. C++万能头文件<bits/stdc++.h>的内容与优缺点

    最近发现了一个C++的头文件bits/stdc++.h,听说这是一个几乎包含了所有C++库函数的头文件,就想更深入的了解一下,下面是头文件内容 // C++ includes used for pre ...

  9. “org/apache/commons/logging/LogFactory”错误的解决方式

    用spring-framework-4.2.6.RELEASE-dist时,发生了如下的错误: [java] view plain copy Exception in thread "mai ...

  10. Python练习_数据类型_day4

    题目 1.作业 1,写代码,有如下列表,按照要求实现每一个功能 li = ["alex", "WuSir", "ritian", " ...