zoukankan      html  css  js  c++  java
  • 洛谷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_{jigoplus k=i}A_j imes B_k),分别当(igoplus)是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)个数,分别代表(igoplus)是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
    说明/提示
    (nle 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 imes FWT[B]_i)
    (=sum_{j|i=i}A_j imes 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("
    ");
    

    实际上这个步骤是通用的,都是一个模样。只不过求(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
    先定义(xigotimes y=count(x&y)\%2),其中(count)表示的是二进制下1的个数,如果它是奇数的话就是1,否则就是0。则可得到性质
    ((aigotimes b) wedge (aigotimes c)=aigotimes (bwedge c))
    它的构造就是(FWT[A]_i=sum_{iigotimes j=0}A_j-sum_{iigotimes j=1}A_j)。进行验证的话就是
    (FWT[C]_i=FWT[A]_i imes FWT[B]_i)
    (=(sum_{iigotimes j=0}A_j-sum_{iigotimes j=1}A_j)(sum_{iigotimes j=0}B_j-sum_{iigotimes j=1}B_j))
    (sum_{iigotimes j=0}A_jsum_{iigotimes k=0}B_k-sum_{iigotimes j=0}A_jsum_{iigotimes k=1}B_k-sum_{iigotimes j=1}A_jsum_{iigotimes k=0}B_k+sum_{iigotimes j=1}A_jsum_{iigotimes k=1}B_k)
    (=sum_{iigotimes(jwedge k)=0}A_jB_k-sum_{iigotimes (jwedge 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("
    ");
    
        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("
    ");
    
        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("
    ");
        return 0;
    }
    
  • 相关阅读:
    html的转码玉反转码
    获取url据对路径写法
    CSS 外边距合并
    页面禁制选中元素的 背景变蓝的通用写法
    centos7.3上安装oracle11.2.4RAC
    通过ansible检查所有服务器根目录磁盘使用情况
    解决es集群启动完成后报master_not_discovered_exception(hostname有错误)
    tidb4.0执行大型sql报没有tmp目录错处理(ERROR 1105 (HY000): open /tmp/1000_tidb/MC4wLjAuMDo0MDAwLzAuMC4wLjA6MTAwODA)
    aix磁盘创建pv、lv
    aix6.1安装oracle
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/13472894.html
Copyright © 2011-2022 走看看