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

题目链接:https://www.luogu.com.cn/problem/P4717
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13472894.html

题目描述

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

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

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

输入
2
2 4 6 8
1 3 5 7

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

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

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

实际上这个步骤是通用的,都是一个模样。只不过求 F W T 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 b\&a=a c & a = a c\&a=a 则有 ( b & c ) & a = a (b\&c)\&a=a ,那么我们就可以对构造 F W T [ A ] i = j & i = i A j 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 y = c o u n t ( x & y ) % 2 x\bigotimes y=count(x\&y)\%2 ,其中 c o u n t count 表示的是二进制下1的个数,如果它是奇数的话就是1,否则就是0。则可得到性质
( a b ) ( a c ) = a ( b c ) (a\bigotimes b) \wedge (a\bigotimes c)=a\bigotimes (b\wedge c)
它的构造就是 F W T [ A ] i = i j = 0 A j i j = 1 A j FWT[A]_i=\sum_{i\bigotimes j=0}A_j-\sum_{i\bigotimes j=1}A_j 。进行验证的话就是
F W T [ C ] i = F W T [ A ] i × F W T [ B ] i FWT[C]_i=FWT[A]_i\times FWT[B]_i
= ( i j = 0 A j i j = 1 A j ) ( i j = 0 B j i j = 1 B j ) =(\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)
i j = 0 A j i k = 0 B k i j = 0 A j i k = 1 B k i j = 1 A j i k = 0 B k + i j = 1 A j i k = 1 B k \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
= i ( j k ) = 0 A j B k i ( j k ) = 1 A j 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;
}

猜你喜欢

转载自blog.csdn.net/qq_43906000/article/details/107923358
今日推荐