【CF449D】Jzzhu and Numbers
阿新 • • 發佈:2021-01-20
題目
題目連結:https://codeforces.com/problemset/problem/449/D
給定一個長度為 \(n\) 的序列 \(a\),求有多少種方案從 \(\{a_i\}\) 裡面選出一個非空子集使這些數按位與起來為 \(0\)。
\(n,a_i\leq 10^6\)。
思路
我們可以把選擇第 \(i\) 個數看做 and 上 \(a_i\),不選擇第 \(i\) 個數看做 and 上 \(\mathrm{lim}=1048575\)。
可以看做 \(n\) 個多項式進行 FWT,其中第 \(i\) 個多項式只有 \(a_i\) 和 \(\mathrm{lim}\) 兩位為 \(1\)
直接做 \(n\) 次 FWT 顯然無法保證複雜度,考慮如何利用最多隻有兩位為 \(1\) 的性質。
因為只有兩位為 \(1\),所以 \(FWT(A_i)_j=\sum^{\mathrm{lim}}_{k=0}c(j,k)[a_i=k]+c(j,\mathrm{lim})=c(j,a_i)+c(j,\mathrm{lim})\)。
因為 and 卷積等價於 \(FWT(A)_i=\sum_{i\in j}\mathrm{val_j}\),而 \(\mathrm{lim}\) 包含 \(1\sim lim\) 所有整數,所以 \[FWT(A_i)_j=c(j,a_i)+c(j,\mathrm{lim})=1+c(j,a_i) \]
我們只需要考慮第 \(j\) 項被多少 \(a_i\) 包含,因為只有這些 \(a_i\) 的貢獻是 \(2\),其餘均為 \(1\)。然後快速冪計算即可。
那麼直接上 and FWT 就好了。時間複雜度 \(O(n\log n)\)。
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1050010,MOD=1000000007; const int C[2][2][2]={{{1,1},{0,1}},{{1,MOD-1},{0,1}}}; int n,lim; ll f[N]; ll fpow(ll x,ll k) { ll ans=1; for (;k;k>>=1,x=x*x%MOD) if (k&1) ans=ans*x%MOD; return ans; } void FWT(ll *f,int typ) { for (int k=1;k<lim;k<<=1) for (int i=0;i<lim;i+=(k<<1)) for (int j=0;j<k;j++) { ll x=f[i+j],y=f[i+j+k]; f[i+j]=(x*C[typ][0][0]+y*C[typ][0][1])%MOD; f[i+j+k]=(x*C[typ][1][0]+y*C[typ][1][1])%MOD; } } int main() { scanf("%d",&n); for (int i=1,x;i<=n;i++) { scanf("%d",&x); f[x]++; } lim=1048576; FWT(f,0); for (int i=0;i<lim;i++) f[i]=fpow(2,f[i]); FWT(f,1); printf("%lld",(f[0]%MOD+MOD)%MOD); return 0; }