2020-2021 ICPC 亚洲区域赛 济南站 L.Bit Sequence

我搬运我自己应该算原创吧

题目

  • 题意
  • 题解

题意

f ( x ) f(x) f(x) x x x二进制下 1 1 1的个数. 给出长度为 m m m 01 01 01序列 a a a, 给出 L L L, 求有多少个 x ∈ [ 0 , L ] x \in [0, L] x[0,L], 满足 f ( x + i ) m o d    2 = a i f(x + i) \mod 2 = a_i f(x+i)mod2=ai

1 ≤ m ≤ 100 , 0 ≤ L ≤ 1 0 18 1 \le m \le 100, 0 \le L \le 10^{18} 1m100,0L1018

题解

g ( i ) = f ( i ) m o d    2 g(i) = f(i) \mod 2 g(i)=f(i)mod2

我们把 g ( i ) g(i) g(i)求出 [ 0 , L + m ] [0, L+m] [0,L+m]个, 排成一排, 然后就类似于字符串匹配一样统计个数.

注意到 L L L很大, 我们没办法把这个序列全部求出来. 所以这个序列一定有规律.

注意到, 当 i ∈ [ 2 k − 1 , 2 k ) i \in [2^{k-1}, 2^k) i[2k1,2k)时, 有 f ( i + 2 k ) = f ( i ) + 1 f(i + 2^k) = f(i) + 1 f(i+2k)=f(i)+1, 也就是相当于 i i i的二进制下的第 k k k位从 0 0 0变成 1 1 1, 所以有 g ( i + 2 k ) = g ( i ) ⊕ 1 g(i + 2^k) = g(i) \oplus 1 g(i+2k)=g(i)1. 这样, 我们就发现了序列 g g g的构造方式:

第一个是0, 然后把当前序列取反, 拼在后面. 如下:

0
01
0110
01101001
0110100110010110
...

还注意到 m m m 很小, 只有 100 100 100, 而 2 7 = 128 2^7 = 128 27=128. 所以我们可以先构造出 g g g的前 128 128 128位, 更大的数一定会包含这个序列多次. 用 h ( i ) h(i) h(i)表示 g g g的前 2 i 2^i 2i位的序列, h ′ ( i ) h'(i) h(i)表示 h ( i ) h(i) h(i)每一位取反的序列.

很容易知道 h ( 8 ) = h ( 7 ) + h ′ ( 7 ) h(8) = h(7) + h'(7) h(8)=h(7)+h(7). 为方便, 令 A = h ( 7 ) , A ′ = h ′ ( 7 ) A = h(7), A' = h'(7) A=h(7),A=h(7).

h ( i ) h(i) h(i)长这样 A A ′ A ′ A A ′ A A A ′ … AA' A' AA' AAA' \dots AAAAAAAA

再令 d p ( X ) dp(X) dp(X) a a a在序列 X X X中出现的次数, 那么有:

d p ( h ( i ) ) = d p ( h ( i − 1 ) ) + d p ( h ′ ( i − 1 ) ) + a 在 h ( i − 1 ) 和 h ′ ( i − 1 ) 交界部分出现的次数 , i ≥ 8 dp(h(i)) = dp(h(i-1)) + dp(h'(i-1)) + a\text{在}h(i-1)\text{和}h'(i-1)\text{交界部分出现的次数}, i \ge 8 dp(h(i))=dp(h(i1))+dp(h(i1))+ah(i1)h(i1)交界部分出现的次数,i8

这个式子可以直接推到底, 变成 d p ( h ( i ) ) = 2 i − 8 ( d p ( A ) + d p ( A ′ ) ) + 所有交界处的贡献 dp(h(i)) = 2^{i-8} (dp(A) + dp(A')) + \text{所有交界处的贡献} dp(h(i))=2i8(dp(A)+dp(A))+所有交界处的贡献

交界一共有四种情况:

  1. A A ′ A A' AA
  2. A ′ A A' A AA
  3. A ′ A ′ A' A' AA
  4. A A AA AA

P P P A A A的长度为 m − 1 m-1 m1的前缀, S S S A A A的长度为 m − 1 m-1 m1的后缀, P ′ , S ′ P', S' P,S为取反.

那么实际上我们求出 a a a S P SP SP中出现的次数, 就是 A A AA AA交界处的贡献了. 而且取 m − 1 m-1 m1, 正好算出 S P SP SP的所有次数不会在 A A A或者 A ′ A' A计算到.

d p ( S P ) dp(SP) dp(SP)等可以求出.

那么求所有交界处的贡献无非就是求 d p ( S P ) dp(SP) dp(SP)系数.

F ( i , 0 / 1 / 2 / 3 ) F(i, 0/1/2/3) F(i,0/1/2/3)分别为序列 h ( i ) h(i) h(i)中, S P ′ , S ′ P , S ′ P ′ , S P SP', S' P, S' P', SP SP,SP,SP,SP的出现次数.

h ( i ) h(i) h(i)的前半段和 h ( i − 1 ) h(i-1) h(i1)一样, 后半段是 h ′ ( i − 1 ) h'(i-1) h(i1), 所以首先有:

F ( i , 1 ) = F ( i , 0 ) = F ( i − 1 , 0 ) + F ( i − 1 , 1 ) F(i, 1) = F(i, 0) = F(i-1, 0) + F(i-1, 1) F(i,1)=F(i,0)=F(i1,0)+F(i1,1)
F ( i , 2 ) = F ( i , 3 ) = F ( i − 1 , 2 ) + F ( i − 1 , 3 ) F(i, 2) = F(i, 3) = F(i-1, 2) + F(i-1, 3) F(i,2)=F(i,3)=F(i1,2)+F(i1,3)

还差一个 h ( i ) h(i) h(i) h ′ ( i ) h'(i) h(i)交界处的.

h ( i ) h(i) h(i)第一个一定是 A A A, 所以 h ′ ( i ) h'(i) h(i)的第一个一定是 A ′ A' A. 可以发现, h ( i ) h(i) h(i)的最后一个和 i i i有关, i = 8 i=8 i=8时, 是 A ′ A' A, i = 9 i = 9 i=9时是 A A A, i = 10 i=10 i=10时是 A ′ A' A, … \dots

易得, i i i为偶数时, F ( i , 0 ) F(i, 0) F(i,0)多加一下, i i i为奇数时, F ( i , 2 ) F(i, 2) F(i,2)多加一下.

再设一个 G ( i , 0 / 1 / 2 / 3 ) = F ( i , 1 / 0 / 3 / 2 ) G(i, 0/1/2/3) = F(i, 1/0/3/2) G(i,0/1/2/3)=F(i,1/0/3/2), 表示 h ′ ( i ) h'(i) h(i)中四种交界的出现次数, 待会要用到.

我们把 L L L进行二进制拆分, 从大到小枚举位置.

假如第 11 , 9 , 8 11, 9, 8 11,9,8位都是 1 1 1, 那么序列前面应该是 h ( 11 ) h ′ ( 9 ) h ( 8 ) h(11)h'(9)h(8) h(11)h(9)h(8)(因为取反), 用 cnt 记录这个位置是否需要取反.

同时需要注意, h ( i ) h ( j ) h(i)h(j) h(i)h(j) 交界处也要算上, 也是有四种情况, 讨论一下. 不需要推导, 只需要手写一些小数据, 然后看每个情况就行.

我们只做到了序列 g g g的前 ∑ i ≥ 8 k i ⋅ 2 i \sum_{i \ge 8} k_i \cdot 2^i i8ki2i( k i k_i ki L L L二进制下第 i i i位的数字), 还有后面一堆没算. 设后面一堆为 R R R, R R R一定是 h ( 8 ) h(8) h(8)或者 h ′ ( 8 ) h'(8) h(8)的前缀(具体是哪一个还是要讨论). 而已经算过的序列的最后是 A A A或者 A ′ A' A(具体是哪一个依然需要讨论), 把前面的长度为 m − 1 m-1 m1的后缀和 R R R拼起来, 暴力求一下, 就解决了 R R R以及做过的序列和 R R R交界处的贡献.

UPD: 注意, 如果第 8 8 8位及以上都没有 1 1 1的话, 就不用取后缀了.

当然 L + m L+m L+m不足 128 128 128的时候暴力就行.

复杂度 O ( m 2 + log ⁡ L ) O(m^2 + \log L) O(m2+logL)

UPD: 被wmx hack了qaq

1
2 148
0 0

24

这里就是因为取了不应该取的后缀, 前面多了一个 0 0 0, 又由于第一位是 0 0 0, 所以多了一个 00 00 00

{{% code %}}

const int MAXN = 70;
const int MAXM = 1e3+10;

int t, m;
LL f[10][MAXN][10], a[MAXM], occ[10], ocsum;
LL L, ans = 0;
VI A, B, C[10], D, E;

VI flip(VI X) {
	VI res;
	for (auto x : X)
		res.push_back(x^1);
	return res;
}

LL calc(VI X) {
	LL res = 0;
	for (int i = 0; i < (int)X.size() - m + 1; i++) {
		int flag = 1;
		for (int j = 0; j < m; j++) {
			if (X[i+j] != a[j]) {
				flag = 0;
				break;
			}
		}
		res += flag;
	}
	return res;
}

int main() {
	scanf("%d", &t);
	f[0][8][0] = 1;
	for (int i = 9; i <= 63; i++) {
		for (int j = 0; j < 4; j++)
			f[0][i][j] = f[0][i-1][j] + f[0][i-1][j^1];
		f[0][i][i&1 ? 2 : 0]++;
	}
	for (int i = 8; i <= 63; i++)
		for (int j = 0; j < 4; j++)
			f[1][i][j] = f[0][i][j^1];
	A.push_back(0);
	for (int i = 1; i <= 7; i++) {
		B = flip(A);
		A.insert(A.end(), B.begin(), B.end());
	}
	B = flip(A);
	D = A;
	D.insert(D.end(), B.begin(), B.end());
	E = flip(D);
	while (t--) {
		scanf("%d%lld", &m, &L);
		L += m;
		for (int i = 0; i < m; i++)
			scanf("%d", a + i);
		ans = 0;
		if (L <= 128) {
			VI v;
			for (int i = 0; i < L; i++)
				v.push_back(A[i]);
			ans = calc(v);
		}
		else {
			ocsum = calc(A) + calc(B);
			for (int i = 0; i < 4; i++)
				C[i].clear();

			for (int i = 1; i <= m-1; i++)
				C[0].push_back(A[128-i]);
			reverse(C[0].begin(), C[0].end());
			for (int i = 0; i < m-1; i++)
				C[0].push_back(B[i]);
			C[1] = flip(C[0]);

			for (int i = 1; i <= m-1; i++)
				C[2].push_back(B[128-i]);
			reverse(C[2].begin(), C[2].end());
			for (int i = 0; i < m-1; i++)
				C[2].push_back(B[i]);
			C[3] = flip(C[2]);

			for (int i = 0; i < 4; i++)
				occ[i] = calc(C[i]);
			int cnt = 0, last = -1;
			for (int i = 63; i >= 8; i--) {
				if ((1ll<<i) & L) {
					ans += ocsum * (1ll << (i - 8));
					for (int j = 0; j < 4; j++) {
						ans += occ[j] * f[cnt&1][i][j];

					}
					if (cnt) {
						if (cnt&1 && last^(cnt&1))
							ans += occ[2];
						else if (cnt&1 && last^(cnt&1)^1)
							ans += occ[0];
						else if ((cnt&1)^1 && last^(cnt&1))
							ans += occ[1];
						else
							ans += occ[3];
					}
					cnt++;
					last = i&1;
				}
			}
			VI &X = (cnt&1)^last ? B : A, &Y = cnt&1 ? E : D;
			int ret = (L & ((1ll << 8) - 1));
			VI Z;
			if (cnt)	// UPD 前面没有, 不用取后缀
				for (int i = 1; i <= m-1; i++)
					Z.push_back(X[128-i]);
			reverse(Z.begin(), Z.end());
			for (int i = 0; i < ret; i++)
				Z.push_back(Y[i]);
			ans += calc(Z);
		}
		printf("%lld\n", ans);
	}
	return 0;
}

{{% /code %}}

你可能感兴趣的:(2020-2021 ICPC 亚洲区域赛 济南站 L.Bit Sequence)