一道容斥神仙題
阿新 • • 發佈:2020-08-08
題意:
簡略題意:
n <= 1e6,0 <= A[i] <= 1e6
首先有一個很顯然的n * A[i]的子集DP,這裡就不再贅述
這題的難點在於把數字看成集合,然後求若干個集合並起來為空的方案數,並起來為空很不想,我們可以考慮並起來恰好有0個元素,"恰好"是不是有二次項反演那感覺了?可惜這題限制條件太多,不好反演或者反演不了
但這給我們啟發,畢竟一切二次項反演題基本都可以用容斥來做,我們可以考慮用容斥來做,加上並起來恰好為0個元素的,減去恰好為1個元素的......所以我們現在要求有多少個集合並起來至少有i個元素,轉化為數字即(bit(x) == i)
(bit[x]即x的二進位制下有幾個一),我們可以先欽定有x個元素,即列舉x的超集,設為dp[x],從數字上也可以理解為(滿足A[i] & x == A[i])的i有多少個,這個顯然可以列舉子集暴力DP,然後每個x的超集可以可選可不選,但不能全不選(全不選並完就變成了空集)
所以對於單個數x貢獻是2^dp[x] - 1;然後容斥一下即可(注意在集合和數字間轉化,個人覺得是本題最大難點)
程式碼如下:
/*陰影之內*/ #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; #define ll long long int read(){ char c = getchar(); int x = 0; while(c < '0' || c > '9') c = getchar() ; while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar() ; return x; } const int maxn = 1e6 + 10; const int mod = 1e9 + 7; int dp[maxn]; int mi[maxn]; void init(){ mi[0] = 1; for(int i = 1; i <= maxn - 10; ++i) mi[i] = 2ll * mi[i-1] % mod; } int main(){ int n = read(); init(); for(int i = 1; i <= n; ++i){ int x = read();dp[x]++; } for(int i = 0; i <= 20; ++i) for(int j = 0; j <= maxn - 10; ++j) if(j & (1 << i)) dp[j^(1<<i)] += dp[j]; ll Ans = 0; for(int i = 0; i <= maxn - 10; ++i){ int num = 0; for(int j = 0; j <= 20; ++j) if(i & (1 << j)) num++; if(num & 1) Ans = (Ans - (mi[dp[i]] - 1) + mod) % mod; else Ans = (Ans + (mi[dp[i]] - 1)) % mod; } cout<<Ans<<endl; return 0; }