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;
}
原文地址:https://www.cnblogs.com/JustinRochester/p/14599371.html