子集卷積淺談
阿新 • • 發佈:2022-02-20
簡介
自己卷積解決的是一類這樣的問題,就兩個集合的無交併,用多項式來表示就是:
\[ f_{k}=\sum\limits_{i \& j=\varnothing,i|j=k}a_ib_j \]做法
我們發現,如果沒有第一個限制,這個就是一個簡單的 FWT 就可以解決,我們考慮一下如何轉化第一個限制,實際上這個限制等同於 \(i\) 的 1 的個數加上 \(j\) 的 1 的個數等於 \(k\) 的 1 的個數。
所以我們設 \(a_{i,j}\) 表示 \(j\) 1 的個數為 \(i\) 位置上的值,只要不符合定義,就是 \(0\)。
整個式子變成了:
\[ f_{k,s}=\sum\limits_{cnt_i+cnt_j=cnt_k,i|j=s}a_{cnt_i,i}b_{cnt_j,j} \]注意到 FWT 的線性性,我們可以在 \(O(n^22^n)\)
程式碼:
#include<bits/stdc++.h> #define dd double #define ld long double #define ll long long #define uint unsigned int #define ull unsigned long long #define N 21 #define M 1100000 using namespace std; const int INF=0x3f3f3f3f; const int mod=1e9+9; template<typename T> inline void read(T &x) { x=0; int f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c == '-') f=-f; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f; } int n,a[N][M],b[N][M],Cnt[M],c[N][M]; inline void FWT(int *f,int n,int op){ for(int mid=1;mid<=(n>>1);mid<<=1) for(int l=0;l<n;l+=(mid<<1)) for(int i=l;i<=l+mid-1;i++){ if(op==0){(f[i+mid]+=f[i])%=mod;} else{(f[i+mid]-=f[i])%=mod;} } } signed main(){ // freopen("my.in","r",stdin); // freopen("my.out","w",stdout); read(n); for(int i=1;i<=(1<<20);i++){ Cnt[i]=__builtin_popcount(i); } for(int i=0;i<(1<<n);i++) read(a[Cnt[i]][i]); for(int i=0;i<(1<<n);i++) read(b[Cnt[i]][i]); n=1<<n; for(int i=0;i<=20;i++) FWT(a[i],n,0); // for(int i=0;i<n;i++) printf("%d ",a[0][i]);puts(""); for(int i=0;i<=20;i++) FWT(b[i],n,0); // for(int i=0;i<n;i++) printf("%d ",b[0][i]);puts(""); for(int i=0;i<=20;i++){ for(int j=0;i+j<=20;j++){ for(int k=0;k<n;k++) c[i+j][k]=(c[i+j][k]+1ll*a[i][k]*b[j][k]%mod)%mod; } } // for(int i=0;i<n;i++) printf("%d ",c[0][i]);puts(""); for(int i=0;i<=20;i++) FWT(c[i],n,1); for(int i=0;i<n;i++) printf("%d ",(c[Cnt[i]][i]%mod+mod)%mod); return 0; }
例題
CF1034E
這個題是非常巧妙的想法,裸的子集卷積,但是原來的複雜度過不去,強迫我們必須利用模數為 \(4\) 的這個特點。所以我們令 \(a_i:=a_i\times 4^{f(i)}\) 其中 \(f(i)\) 是 \(i\) 在二進位制下 \(1\) 的個數,這個做法之所以不能擴充套件的原因是這個做法不能夠中途取模。
容易發現爆 ll,經過分析,發現用 ull 是正確的,如果指數比較大,就已經是 \(0\) 了,否則,容易發現沒有影響。
#include<bits/stdc++.h> #define dd double #define ld long double #define ll long long #define uint unsigned int #define int long long #define ull unsigned long long #define N 2100000 #define M number using namespace std; const int INF=0x3f3f3f3f; template<typename T> inline void read(T &x) { x=0; int f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c == '-') f=-f; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f; } inline void FWT(ull *f,int n,int op){ for(int mid=1;mid<=(n>>1);mid<<=1) for(int l=0;l<n;l+=(mid<<1)) for(int k=l;k<=l+mid-1;k++){ // printf("k+mid=%d k=%d\n",k+mid,k); if(op==0) f[k+mid]+=f[k]; else f[k+mid]-=f[k]; } } ull a[N],b[N],c[N]; int n; char s[N],t[N]; signed main(){ read(n);n=1<<n; scanf("%s%s",s,t); for(int i=0;i<n;i++) a[i]=((ull)(s[i]-'0'))<<(__builtin_popcountll(i)<<1); for(int i=0;i<n;i++) b[i]=((ull)(t[i]-'0'))<<(__builtin_popcountll(i)<<1); // for(int i=0;i<n;i++) printf("%d ",a[i]);puts(""); // for(int i=0;i<n;i++) printf("%d ",b[i]);puts(""); FWT(a,n,0);FWT(b,n,0); for(int i=0;i<n;i++){ c[i]=a[i]*b[i]; // printf("i=%d a=%d b=%d c=%d\n",i,a[i],b[i],c[i]); } FWT(c,n,1); for(int i=0;i<n;i++) c[i]=(c[i]/((ull)1<<(__builtin_popcountll(i)<<1)))&3; for(int i=0;i<n;i++) printf("%llu",c[i]); return 0; }