[ICPC]2020瀋陽L.Bit Sequence
阿新 • • 發佈:2021-10-31
題意:
給定一個長度為\(m\)的序列\(a\),問\([0,L]\)中有多少個數\(x\)滿足\(popcount(x+i)\mod 2 = a_i\)。
\(L\le 10^{18}\)
\(m\le 100\)
題解:
想一會兒之後會注意到\(m\le 100\)。
顯然如果涉及到加法的popcount很難計算,可以想辦法轉化為二進位制下的構造。
我們把最後7位單獨拎出來,這樣前面的位就跟最後一坨東西關係變小了不少。
我們把最後尾巴上的一點特例給暴力算了之後,\(l\)的最後7位就是0了,然後我們只需要考慮每個0到128的變化過程。
這時候,由於\(128>100\),所以在一段中,每個數往後\(m\)
這個改變只有4種:0變成0,0變成1,1變成0,1變成1.
可以算出在每種改變下,這一段中的滿足的數的個數。
於是我們只需要計算每種改變有多少個即可。
這玩意就可以通過一些數位dp的技巧求了。
首先我們預處理出\([0,2^k)\)(這是去掉後7位以後)中每種變化有多少個。
這個很好dp:
\(f[i][0/1][0/1]\)表示\([0, 2^i)\)中每種變化有多少個。
還有一個額外的轉移:
\[f[i][(i - 1) \& 1][1] += 1 \]然後計算\([0,L)\)
由於上面dp是不包含\(2^i\)次的,還需要再額外轉移一下區間末尾的情況。
程式碼:
#include <bits/stdc++.h> #define get(x) (popcount(x) & 1) #define int long long #define pt(x) cout << x << endl; #define Mid ((l + r) / 2) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; const int N = 109; int m, l, a[N], ans; int f[60][2][2]; int popcount(int x) { return __builtin_popcount(x >> 32) + __builtin_popcount(x & ((1ll << 32) - 1)); } int cal(int l, int r) { int ans = 0; for(int i = l; i <= r; i++) { int f = 1; for(int j = 0; j < m; j++) { if(a[j] != get(i + j)) { f = 0; break; } } ans += f; } return ans; } void work() { int cnt[2][2] = {0}; ans = 0; scanf("%lld%lld", &m, &l); for(int i = 0; i < m; i++) scanf("%d", &a[i]); if(l < 200) { ans = cal(0, l); printf("%lld\n", ans); return ; } for(int i = 0; i <= 1; i++) { for(int k = 0; k <= 1; k++) { for(int p = 0; p < 128; p++) { int f = 1; for(int j = 0; j < m; j++) { int t = get(((1ll << 7) - 1) & (p + j)); if(p + j >= 128) t ^= k; else t ^= i; if(t != a[j]) { f = 0; break; } } cnt[i][k] += f; } } } int t = ((1ll << 7) - 1) & l; ans = cal(l - t, l); l >>= 7; int ac[2][2] = {0}, now = 0; for(int i = 60; i >= 0; i--) if(l >> i & 1) { int t = get(now); for(int qwq = 0; qwq <= 1; qwq++) for(int qaq = 0; qaq <= 1; qaq++) ac[qwq ^ t][qaq ^ t] += f[i][qwq][qaq]; now += 1ll << i; ac[get(now - 1)][get(now)]++; } for(int qwq = 0; qwq <= 1; qwq++) for(int qaq = 0; qaq <= 1; qaq++) ans += ac[qwq][qaq] * cnt[qwq][qaq]; printf("%lld\n", ans); return ; } signed main() { for(int i = 1; i <= 60; i++) { for(int j = 0; j <= 1; j++) { for(int k = 0; k <= 1; k++) { f[i][j][k] = f[i - 1][j][k] + f[i - 1][j ^ 1][k ^ 1]; } } f[i][(i - 1) & 1][1] += 1; } int Case; scanf("%lld", &Case); while(Case--) work(); return 0; }