P9017 [USACO23JAN] Lights Off G 题解

Description

给定正整数 NN,和两个长为 NN0101 序列 aabb。定义一次操作为:

  1. bb 序列中的一个值翻转(即 00 变成 1111 变成 00,下同)。
  2. 对于 bb 序列中每个值为 11 的位置,将 aa 序列中对应位置的值翻转。
  3. bb 序列向右循环移位 11 位。即若当前 bb 序列为 b1b2bnb_1b_2\cdots b_{n},则接下来变为 bnb1b2bn1b_{n}b_1b_2\cdots b_{n-1}

TT 次询问,对每一次询问,你需要回答出至少需要几次操作,才能使 aa 序列中每一个位置的值都变为 00

link

Solution

显然可以把 a,ba,b 数组看成两个数,操作一就是对 bb 的某一位取反,操作二就是让 aa 异或 bb,操作三是让 bb2b\leftarrow \left\lfloor \frac{b}{2} \right\rfloor

容易发现操作数不超过 3n3n,因为可以先用至多 nn 次操作把 bb 变成 00。然后每连续两次操作就让 bb 的某一位变成 11,把 aa 的这一位消掉,然后 bb 清空。

然而这样做是 O(Tnn)O(T\cdot n^n) 的,过不了且没法优化。


观察可知,如果第 ii 次操作将第 jj 位异或 11,总共进行 ss 次操作,那么这次操作对最终 aa 的贡献就是 jj+sij\sim j+s-i 这些位取反(在模 nn 意义下)。

这样就可以 dp 了。

fi,jf_{i,j} 表示进行恰好 ii 次操作,能否让 aa 变成 jj,设 xx 为任意一个模 nn 意义下连续的长度为 11 的数组所对应的状态,那么就让 fi,jfi1,jxbf_{i,j}\leftarrow f_{i-1,j\oplus x\oplus b}

初始 f0,a=1f_{0,a}=1,时间复杂度:O(Tn2n)O(T\cdot n\cdot 2^{n}),过不了。


考虑把操作一的贡献和操作二、三的贡献拆开算。操作一所做的贡献就相当于初始 b=0b=0 进行若干次操作对 aa 的贡献,显然可以预处理,即设 fi,jf_{i,j} 表示 bb 初始值为 00,对 aa 能否做出 jj 的贡献。

xx 为任意一个模 nn 意义下连续的长度为 11 的数组所对应的状态,那么就让 fi,jfi1,jxf_{i,j}\leftarrow f_{i-1,j\oplus x}

而操作二、三就是对 bb 进行这么多操作的异或和,枚举操作次数即可求得。

时间复杂度:O(n22n+Tn)O(n^2\cdot 2^n+Tn)

具体实现细节见代码

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <cstdio>
#include <iostream>
#include <map>

// #define int int64_t

int n, a, b;
int f[100][1 << 20];

int shift(int x) {
return (x >> 1) + (1 << n - 1) * (x & 1);
}

int tonum(std::string s) {
int ret = 0;
for (int i = 0; i < static_cast<int>(s.size()); ++i)
ret = (ret << 1) + s[i] - '0';
return ret;
}

int calc(int a, int b) {
for (int i = 0; i <= 3 * n; ++i, b = shift(b)) {
if (f[i][a]) return i;
a ^= b;
}
return -1;
}

void prework() {
f[0][0] = 1;
for (int i = 1; i <= 3 * n; ++i) {
int s = (1 << ((i - 1) % n + 1)) - 1;
for (int j = 0; j < n; ++j, s = shift(s)) {
for (int k = 0; k < (1 << n); ++k)
f[i][k] |= f[i - 1][k ^ s];
}
}
}

void dickdreamer() {
int t;
std::cin >> t >> n;
prework();
for (; t; --t) {
std::string s, t;
std::cin >> s >> t;
a = tonum(s), b = tonum(t);
std::cout << calc(a, b) << '\n';
}
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P9017 [USACO23JAN] Lights Off G 题解
https://sobaliuziao.github.io/2023/07/27/post/527e3a29.html
作者
Egg_laying_master
发布于
2023年7月27日
许可协议