FWT 等总结

目录

  • 与卷积:

    • 代码:
  • 或卷积:
    • 代码:
  • 异或卷积:
    • 代码:

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];

原文地址:https://www.cnblogs.com/lnzwz/p/11257691.html

时间: 2024-10-07 07:31:28

FWT 等总结的相关文章

fwt优化+树形DP HDU 5909

1 //fwt优化+树形DP HDU 5909 2 //见官方题解 3 // BestCoder Round #88 http://bestcoder.hdu.edu.cn/ 4 5 #include <bits/stdc++.h> 6 // #include <iostream> 7 // #include <cstdio> 8 // #include <cstdlib> 9 // #include <algorithm> 10 // #inc

HDU 5823 color II(FWT)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5823 [题目大意] 定义一张无向图的价值:给每个节点染色使得每条边连接的两个节点颜色不相同的最少颜色数. 对于给定的一张由n个点组成的无向图,求该图的2^n-1张非空子图的价值. [题解] 设f[i][S]表示i种颜色覆盖S这个集合的方案数,我们只要得到最小的i,f[i][S]大于0,那么i就是S集合的答案.显然有f[i][S]=∑f[1][u]×f[i−1][v](u|v==S),这个怎么求呢

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

感觉快速沃尔什变换和快速傅里叶变换有很大的区别啊orz 不是很明白为什么位运算也可以叫做卷积(或许不应该叫卷积吧) 我是看 http://blog.csdn.net/liangzhaoyang1/article/details/52819835 里的快速沃尔什变换 这里说一下自己的理解吧,快速傅里叶变换是计算卷积的,就是∑f(x)*g(n-x)这种 快速沃尔什变换也是计算∑f(x)*g(y) ,但这里是计算所有的满足x^y = n(卷积是计算x+y=n)的和 当然,异或也可以换成&,|这些运算符

计蒜客16495 Truefriend(fwt)

#include <iostream> #include <cstring> #include <cstdio> using namespace std; typedef long long LL; const int maxn = 1e5 + 100; class FWT{ public: void fwt(LL *a, int n){ for(int d = 1; d < n; d <<= 1){ for(int m = d<<1, i

FWT

FWT模板: 1 void FWT(int a[],int n) 2 { 3 for(int d=1;d<n;d<<=1) //你甚至可以RANDOMSHUFFLE 4 for(int m=d<<1,i=0;i<n;i+=m) 5 for(int j=0;j<d;j++) 6 { 7 int x=a[i+j],y=a[i+j+d]; 8 a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod; 9 //xor:a[i+j]=x+y,a[i+

UOJ 310 黎明前的巧克力(FWT)

[题目链接] http://uoj.ac/problem/310 [题目大意] 给出一个数集,A从中选择一些数,B从中选择一些数,不能同时不选 要求两者选择的数异或和为0,问方案数 [题解] 题目等价于选取一个非空且xor为0的集合并将其拆分为两个子集的方案数 用dp表示xor为j的方案数,易得dp方程dp[i][j]=dp[i-1][j]+2*dp[i-1][j^a[i]] 该式等价于dp数组与只有两个元素有值的g[0]=1,g[a[i]]=2的数组做卷积运算 对g数组进行反演可以发现每次卷积

【CF772D】Varying Kibibits FWT

[CF772D]Varying Kibibits 题意:定义函数f(a,b,c...)表示将a,b,c..的10进制下的每一位拆开,分别取最小值组成的数.如f(123,321)=121,f(530, 932, 81)=30.给你一个数集$T={a_1,a_2...a_n}$,定义函数G(x) 求$G(1)\oplus G(2)\oplus ...G(999999)$. $1\le n \le 1000000,0\le a_i \le 999999$ 题解:发现f函数就是10进制下的按位与,所以我

【洛谷U20626】gemo 容斥 FWT 高斯消元

题目大意 给你一个无向图,有\(m\)个询问,每次给你一个点\(x\)和一个点集\(S\),问你从\(x\)开始走,每次从一个点随机的走到与这个点相邻的点,问你访问\(S\)中每个点至少一次的期望步数是多少. \(n\leq 18,m\leq 100000\) 题解 有个东西叫min-max容斥: \[ \max(S)=\sum_{T\subseteq S}{(-1)}^{|T|+1}\min(T) \] 这道题中,\(\min(S)\)是从点\(x\)开始走,走到\(S\)中任意一个点的期望步

2016vijos 1-3 兔子的晚会(生成函数+倍增FWT)

求出序列的生成函数后,倍增FWT #include<cstdio> using namespace std; #define N 2048 const int mod=1e9+7; int inv; int f[N+1]; int Pow(int a,int b) { int res=1; for(;b;a=1LL*a*a%mod,b>>=1) if(b&1) res=1LL*res*a%mod; return res; } void FWT(int *a,int n) {

FWT快速沃尔什变换学习笔记

FWT快速沃尔什变换学习笔记 1.FWT用来干啥啊 回忆一下多项式的卷积\(C_k=\sum_{i+j=k}A_i*B_j\) 我们可以用\(FFT\)来做. 甚至在一些特殊情况下,我们\(C_k=\sum_{i*j=k}A_i*B_j\)也能做(SDOI2015 序列统计). 但是,如果我们把操作符换一下呢? 比如这样? \(C_k=\sum_{i|j=k}A_i*B_j\) \(C_k=\sum_{i\&j=k}A_i*B_j\) \(C_k=\sum_{i\wedge j=k}A_i*B_