1. 程式人生 > 其它 >[ICPC]2020瀋陽L.Bit Sequence

[ICPC]2020瀋陽L.Bit Sequence

題意:

給定一個長度為\(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\)

個數,最多隻會跨過一段,這一段跨過造成的變化只有:對前面進位,造成1的個數的改變。
這個改變只有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][j][k] = f[i-1][j][k] + f[i-1][j\oplus 1][k\oplus 1] \]

還有一個額外的轉移:

\[f[i][(i - 1) \& 1][1] += 1 \]

然後計算\([0,L)\)

的變化個數,就從高位開始,對於每個\(1\),取0的時候,就加上對應位的變化種數,此時還要注意還要異或上前面1的數量的奇偶性。

由於上面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;
}