• HDU


    传送门


    【分析】

    考虑 ((ivee j)=(iwedge j)vee (ioplus j))

    (x=ivee j,y=ioplus jRightarrow x=yvee kRightarrow k=xoplus y(k,ysubseteq x))

    (quad C_k)

    (displaystyle =sum_{iwedge j=k}A_{ioplus j}B_{ivee j})

    (displaystyle =sum_{xoplus y=k}[ysubseteq x]A_yB_xsum_isum_j[ivee j=x][i wedge j=k])

    (displaystyle =sum_{xoplus y=k}[ywedge x=y]A_yB_x2^{pc(x)-pc(k)})

    其中 (pc(n)) 代表 (n) 二进制中 (1) 的个数

    考虑到 (k,y) 均为 (x) 的子集,且 (k=xoplus y)

    (pc(x)-pc(k)=pc(y)) ,代入原式得到

    (displaystyle C_k=sum_{xoplus y=k\ ywedge x=y}(A_ycdot 2^{pc(y)})cdot B_x)

    考虑到式子的本质,像是对于 (k) ,枚举另一个与它不交的集合 (y) ,把两者的并集和 (y) 集合的贡献统计到 (k)


    类似的东西是普通的子集卷积: (displaystyle C_n=sum_{ivee j=n\ iwedge j=0}A_iB_j)

    这个代表的含义是:将集合 (n) 划分成两部分,将两个部分的贡献统计到 (C_n)

    实现的方法在于:拓广一维,使得统计 (i)(n) 的贡献时,强制使得统计的另一个贡献来源于 ((n-i))

    由于子集卷积是对一个集合的划分,故可以得出:(pc(n)=pc(i)+pc(n-i))

    因此,考虑 (i) 的贡献时,强制其从 (pc(i)) 的维度转移过来,则可强制另一位从 (pc(n-i)) 转移;即可保证 (i)(j) 不交

    实现代码则如下:

    inline int pc(int n) { return __builtin_popcount(n>>10)+__builtin_popcount(n&1023); }
    inline void FST(int *a,int *b,int *c,int bits, int len){
        for(int i=0;i<len;++i) A[pc(i)][i]=a[i];
        for(int i=0;i<len;++i) B[pc(i)][i]=b[i];
        for(int i=0;i<=bits;++i) FWT(A[i], len, 0), FWT(B[i], len, 0);
        for(int x=0;x<=bits;++x)
            for(int y=0;y<=x;++y)
                for(int i=0;i<len;++i)
                    C[x][i]=add( C[x][i] , (ll)A[y][i]*B[x-y][i]%P );
        for(int i=0;i<=bits;++i) FWT(C[i], len, 1);
        for(int i=0;i<len;++i) c[i]=C[pc(i)][i];
    }
    

    总复杂度 (O(nlog^2 n), n=2^bits=len)


    这题类似的处理

    据说叫子集异或卷积,前面的叫子集或卷积

    即考虑 (A_y) 的贡献从 (y) 转移时,强制从 (pc(y)) 转移,另一位 (B_x) 即可强制从 (pc(k+y)) 转移

    inline int pc(int n) { return __builtin_popcount(n>>10)+__builtin_popcount(n&1023); }
    inline void FST(int *a,int *b,int *c,int bits, int len){
        for(int i=0;i<len;++i) A[pc(i)][i]=a[i];
        for(int i=0;i<len;++i) B[pc(i)][i]=b[i];
        for(int i=0;i<=bits;++i) FWT(A[i], len, 0), FWT(B[i], len, 0);
        for(int x=0;x<=bits;++x)
            for(int y=0;y<=x;++y)
                for(int i=0;i<len;++i)
                    C[x-y][i]=add( C[x-y][i] , (ll)A[y][i]*B[x][i]%P );
        for(int i=0;i<=bits;++i) FWT(C[i], len, 1);
        for(int i=0;i<len;++i) c[i]=C[pc(i)][i];
    }
    

    复杂度相同


    【代码】

    最后上总代码,无封装

    #include<bits/stdc++.h> 
    using namespace std;
    typedef long long ll;
    const int MOD=998244353, M=1<<20;
    int m, A[20][M], B[20][M], C[20][M];
    inline int pc(int n) { return __builtin_popcount(n>>10)+__builtin_popcount(n&1023); }
    inline int add(int a,int b) { return (a+b>=MOD)?(a+b-MOD):(a+b); }
    inline int dis(int a,int b) { return (a-b<0)?(a-b+MOD):(a-b); }
    inline int shr(int n,int p) { return p?((n&1)?(n+MOD>>1):(n>>1)):n; }
    
    inline void FWT(int *a,int len,int op){
    	for(int k=0;1<<k<len;++k) for(int i=0;i<len;++i) if(~i>>k&1){
    		int j=i^(1<<k), x, y;
    		x=add(a[i], a[j]), y=dis(a[i], a[j]);
    		a[i]=shr(x,op), a[j]=shr(y,op);
    	}
    }
    int main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0); cout.tie(0);
    	cin>>m;
    	for(int i=0,n;!(i>>m);++i) cin>>A[pc(i)][i], A[pc(i)][i]=A[pc(i)][i]*(1ll<<pc(i))%MOD;
    	for(int i=0,n;!(i>>m);++i) cin>>B[pc(i)][i];
    	for(int i=0;i<=m;++i) FWT(A[i],1<<m,0), FWT(B[i],1<<m,0);
    	for(int x=0;x<=m;++x)
    		for(int y=0;y<=x;++y)
    			for(int i=0;!(i>>m);++i)
    				C[x-y][i]=add(C[x-y][i], (ll)A[y][i]*B[x][i]%MOD);
    	for(int i=0;i<=m;++i) FWT(C[i],1<<m,1);
    	int ans=0;
    	for(int i=(1<<m)-1;~i;--i) ans=add(ans*1526ll%MOD, C[pc(i)][i]);
    	cout<<ans<<"
    ";
    	cout.flush();
    	return 0;
    }
    
  • 相关阅读:
    一种无法被Dump的jar包加密保护解决方案
    基于设备指纹零感验证系统
    IOS防作弊产品技术原理分析
    某移动端防作弊产品技术原理浅析与个人方案构想
    web安全防御之RASP技术
    Linux漏洞分析入门笔记-Off-By-One(栈)
    smb中继学习
    Dedecms sp2 5.7 后台getshell审计
    phpmyadmin后台代码执行分析复现
    静态恶意代码逃逸-学习一
  • 原文地址:https://www.cnblogs.com/JustinRochester/p/14599371.html
Copyright © 2020-2023  润新知