uoj310【UNR #2】黎明前的巧克力(FWT)
阿新 • • 發佈:2020-07-24
uoj310【UNR #2】黎明前的巧克力(FWT)
題解時間
對非零項極少的FWT的優化。
首先有個十分好想的DP: $ f[i][j] $ 表示考慮了前 $ i $ 個且異或和為 $ j $ 的方案數,
有 $ f[i][j]= f[i-1][j] + 2 * f[i-1][j \oplus a[i]] $ 。
可以考慮FWT,但很明顯時間複雜度沒有優化。
但另一方面,每層的卷積卷的都是 $ 1,0,0,...,2,0,0,... $ 的形式,
這樣一來卷之後每項都是 $ -1 $ 或 $ 3 $ 。
因此大膽一點直接把所有的式子加到一起卷積,
卷完有什麼用呢?
這樣卷出來的結果是每層卷積相加,而正常FWT求解是每層卷積相乘。
每一位都是共計 $ n $ 個 $ -1 $ 和 $ 3 $ 加一起,所以可以求出每一位上兩種數的個數。
由於知道了每一位上兩種數的個數,直接每一位快速冪就能求出原來的相乘結果。
#include<bits/stdc++.h> using namespace std; typedef long long lint; struct pat{int x,y;pat(int x=0,int y=0):x(x),y(y){}bool operator<(const pat &p)const{return x==p.x?y<p.y:x<p.x;}}; template<typename TP>inline void read(TP &tar) { TP ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){ret=ret*10+(ch-'0');ch=getchar();} tar=ret*f; } template<typename TP,typename... Args>inline void read(TP& t,Args&... args){read(t),read(args...);} namespace RKK { const int N=2000011; const int defaultlen=1048576; const int mo=998244353,inv2=499122177,inv4=748683265; void doadd(int &a,int b){if((a+=b)>=mo) a-=mo;}int add(int a,int b){return (a+=b)>=mo?a-mo:a;} void domul(int &a,int b){a=1ll*a*b%mo;}int mul(int a,int b){return 1ll*a*b%mo;} int fpow(int a,int p){int ret=1;while(p){if(p&1) domul(ret,a);domul(a,a),p>>=1;}return ret;} int n,a[N]; void fwtxor(int *a,int len,int tp) { for(int i=1,w1,w2;i<len;i<<=1) for(int j=0;j<len;j+=i<<1) for(int k=0;k<i;k++) { w1=a[j+k],w2=a[j+k+i]; a[j+k]=add(w1,w2),a[j+k+i]=add(w1,mo-w2); if(tp==-1) domul(a[j+k],inv2),domul(a[j+k+i],inv2); } } int main() { read(n);for(int i=1,w;i<=n;i++) read(w),a[0]+=1,a[w]+=2; fwtxor(a,defaultlen,1); for(int i=0,cnt1,cnt3;i<defaultlen;i++) { cnt3=mul(add(n,a[i]),inv4),cnt1=n-cnt3; a[i]=(cnt1&1)?mo-fpow(3,cnt3):fpow(3,cnt3); } fwtxor(a,defaultlen,-1); printf("%d\n",add(a[0],mo-1)); return 0; } } int main(){return RKK::main();}