洛谷P4717-快速沃尔什变换/FWT(FWT板子题)

题目链接:https://www.luogu.com.cn/problem/P4717
CSDN食用链接:https://blog.csdn.net/qq_43906000/article/details/107923358

题目描述

给定长度为\(2^n\)两个序列\(A,B\),设\(C_i=\sum_{j\bigoplus k=i}A_j\times B_k\),分别当\(\bigoplus\)是or,and,xor时求出\(C\)

输入格式
第一行一个数n,第二行\(2^n\)个数\(A_0,\cdots,A_{2^n-1}\),第三行\(2^n\)个数\(B_0,\cdots,B_{2^n-1}\)

输出格式
三行每行\(2^n\)个数,分别代表\(\bigoplus\)是or,and,xor时\(C_0,\cdots C_{2^n-1}\)的值\(mod\ 998244353\)

输入
2
2 4 6 8
1 3 5 7

输出
2 22 46 250
88 64 112 56
100 92 68 60
说明/提示
\(n\le 17\)

emmm,真的对于这些构造怎么得来的,不用太过深究,and,or运算还好,但xor的构造。。。我真的看了很久也不太明白怎么构造出来的,为什么是这样的。。。只能说是它是经得起验证的QAQ

对于卷积或:
我们知道或的性质有:\(a|c=c\)\(b|c=c\),则\((a|b)|c=c\),那么我们可以定义一个正变换:\(FWT[A]_i=\sum_{j|i=i}A_j\),那么则有如下证明:
\(FWT[C]_i=FWT[A]_i\times FWT[B]_i\)
\(=\sum_{j|i=i}A_j\times \sum_{k|i=i}B_k\)
\(=\sum_{j|i=i}\sum_{k|i=i}A_jB_k\)
\(=\sum_{(j|k)|i=i}A_jB_k\)
也就是说确实是OK的。那么我们就可以通过一次\(FWT\)的正变换将\(A,B\)转化为\(FWT[A],FWT[B]\),之后我们将其对应的数乘起来就会得到\(FWT[C]\),那么我们就可以再通过一次逆变换将\(FWT[C]\)转换为\(C\),也就是如下片段:

int len=1<<n,typefornt=1,typeback=-1;
FWT::FWTOR(a,len,typefornt);   FWT::FWTOR(b,len,typefornt);
for (int i=0; i<len; i++)
	c[i]=1LL*a[i]*b[i]%mod;
FWT::FWTOR(c,len,typeback);
for (int i=0; i<len; i++)
	printf("%d ",c[i]);
printf("\n");

实际上这个步骤是通用的,都是一个模样。只不过求\(FWT\)的过程就会不太好写,对于or我们可以得到如下的片段:

void FWTOR(int *ary,const int &len,const int &typ) {
	for(int L=2,T=1; L<=len; L<<=1,T<<=1)
		for(int i=0; i<len; i+=L)
			for(int j=i; j<i+T; j++)
				if(typ==1) ary[j+T]=FAdd(ary[j+T],ary[j]);
				else ary[j+T]=FSub(ary[j+T],ary[j]);
}

对于卷积与运算,其性质和或的相似:\(b\&a=a\)\(c\&a=a\)则有\((b\&c)\&a=a\),那么我们就可以对构造\(FWT[A]_i=\sum_{j\&i=i}A_j\)
则有如下代码:

void FWTAND(int *ary,const int &len,const int &typ) {
	for(int L=2,T=1; L<=len; L<<=1,T<<=1)
		for(int i=0; i<len; i+=L)
			for(int j=i; j<i+T; j++)
				if(typ==1) ary[j]=FAdd(ary[j],ary[j+T]);
				else ary[j]=FSub(ary[j],ary[j+T]);
}

接下来就是神仙xor了。。。。我只能orz
先定义\(x\bigotimes y=count(x\&y)\%2\),其中\(count\)表示的是二进制下1的个数,如果它是奇数的话就是1,否则就是0。则可得到性质
\((a\bigotimes b) \wedge (a\bigotimes c)=a\bigotimes (b\wedge c)\)
它的构造就是\(FWT[A]_i=\sum_{i\bigotimes j=0}A_j-\sum_{i\bigotimes j=1}A_j\)。进行验证的话就是
\(FWT[C]_i=FWT[A]_i\times FWT[B]_i\)
\(=(\sum_{i\bigotimes j=0}A_j-\sum_{i\bigotimes j=1}A_j)(\sum_{i\bigotimes j=0}B_j-\sum_{i\bigotimes j=1}B_j)\)
\(\sum_{i\bigotimes j=0}A_j\sum_{i\bigotimes k=0}B_k-\sum_{i\bigotimes j=0}A_j\sum_{i\bigotimes k=1}B_k-\sum_{i\bigotimes j=1}A_j\sum_{i\bigotimes k=0}B_k+\sum_{i\bigotimes j=1}A_j\sum_{i\bigotimes k=1}B_k\)
\(=\sum_{i\bigotimes(j\wedge k)=0}A_jB_k-\sum_{i\bigotimes (j\wedge k)=1}A_jB_k\)
emmmm。。。。
验证的话没什么难度,但是构造就有点迷了QAQ,算了,用着用着就应该熟悉了。
xor卷积代码:

void FWTXOR(int *ary,const int &len,const int &typ) {
	for(int L=2,T=1; L<=len; L<<=1,T<<=1)
		for(int i=0; i<len; i+=L)
			for(int j=i; j<i+T; j++) {
				int Aj=ary[j];
				ary[j]=FAdd(Aj,ary[j+T]);
				ary[j+T]=FSub(Aj,ary[j+T]);
				if(typ==-1)
					ary[j]=FMul(ary[j],INV2),ary[j+T]=FMul(ary[j+T],INV2);
			}
}

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

const int mac=(1<<17)+10;
const int mod=998244353;

int a[mac],b[mac],c[mac];
int aa[mac],bb[mac];

namespace FWT{
    const int MOD=998244353;
    inline int FAdd(const int &a,const int &b){return a+b>=MOD? a+b-MOD:a+b;}
    inline int FSub(const int &a,const int &b){return a-b<0? a-b+MOD:a-b;}
    inline int FMul(const int &a,const int &b){return 1ll*a*b%MOD;}
    inline int FPow(int a,int b){int ret=1;while(b){if(b&1)ret=FMul(ret,a);a=FMul(a,a);b>>=1;}return ret;}
    const int INV2=FPow(2,MOD-2);
    void FWTOR(int *ary,const int &len,const int &typ){
        for(int L=2,T=1;L<=len;L<<=1,T<<=1)
            for(int i=0;i<len;i+=L)
                for(int j=i;j<i+T;j++)
                    if(typ==1) ary[j+T]=FAdd(ary[j+T],ary[j]);
                    else ary[j+T]=FSub(ary[j+T],ary[j]);
    }
    void FWTAND(int *ary,const int &len,const int &typ){
        for(int L=2,T=1;L<=len;L<<=1,T<<=1)
            for(int i=0;i<len;i+=L)
                for(int j=i;j<i+T;j++)
                    if(typ==1) ary[j]=FAdd(ary[j],ary[j+T]);
                    else ary[j]=FSub(ary[j],ary[j+T]);
    }
    void FWTXOR(int *ary,const int &len,const int &typ){
        for(int L=2,T=1;L<=len;L<<=1,T<<=1)
            for(int i=0;i<len;i+=L)
                for(int j=i;j<i+T;j++){
                    int Aj=ary[j];
                    ary[j]=FAdd(Aj,ary[j+T]);
                    ary[j+T]=FSub(Aj,ary[j+T]);
                    if(typ==-1)
                        ary[j]=FMul(ary[j],INV2),ary[j+T]=FMul(ary[j+T],INV2);
                }
    }
}

int main(int argc, char const *argv[])
{
    int n;
    scanf ("%d",&n);
    for (int i=0; i<1<<n; i++)
        scanf ("%d",&a[i]);
    for (int i=0; i<1<<n; i++)
        scanf ("%d",&b[i]);
    for (int i=0; i<1<<n; i++)
        aa[i]=a[i],bb[i]=b[i];

    int len=1<<n,typefornt=1,typeback=-1;
    FWT::FWTOR(a,len,typefornt); FWT::FWTOR(b,len,typefornt);
    for (int i=0; i<len; i++)
        c[i]=1LL*a[i]*b[i]%mod;
    FWT::FWTOR(c,len,typeback);
    for (int i=0; i<len; i++)
        printf("%d ",c[i]); printf("\n");

    for (int i=0; i<1<<n; i++) a[i]=aa[i],b[i]=bb[i];
    FWT::FWTAND(a,len,typefornt); FWT::FWTAND(b,len,typefornt);
    for (int i=0; i<len; i++)
        c[i]=1LL*a[i]*b[i]%mod;
    FWT::FWTAND(c,len,typeback);
    for (int i=0; i<len; i++)
        printf("%d ",c[i]); printf("\n");

    for (int i=0; i<1<<n; i++) a[i]=aa[i],b[i]=bb[i];
    FWT::FWTXOR(a,len,typefornt); FWT::FWTXOR(b,len,typefornt);
    for (int i=0; i<len; i++)
        c[i]=1LL*a[i]*b[i]%mod;
    FWT::FWTXOR(c,len,typeback);
    for (int i=0; i<len; i++)
        printf("%d ",c[i]); printf("\n");
    return 0;
}
posted @ 2020-08-10 23:14  lonely_wind  阅读(226)  评论(0编辑  收藏  举报