只要你跑的够快,锅就追不上你

0%

「EOJ 181B」Bits(数据分治)

题目大意

给定一个长度为 $n$ 的 01 串和一个正整数 $m$。你要进行一系列操作,将串变为有长度为 $m$ 的循环节的串。操作一共有两种:

  • 将串的某一位反转。
  • 将串的一个长度为 $k \times m$ 的前缀全部反转。其中 $1 \le k \le \frac{n}{m}$。

问至少进行几次操作。

数据范围:$n \le 300, m \le n$。

思路分析

我们令 $M = \lfloor \frac{n}{m} \rfloor$。

先考虑 $m$ 较大的情况。

发现第二种操作最多只有 $M$ 种,我们暴力枚举每个操作是否使用,然后对于操作后的串计算使用第一种操作最少的次数即可。复杂度看似是 $2^M \times n$ 的,但实际上可以通过 DFS 来优化到 $O(2^M \times m)$。

再考虑 $m$ 较小的情况。

发现最终得到的串最多只有 $2^m$ 种,我们枚举每个串,然后处理出那些位置需要被反转,再用 DP 来计算最少修改次数即可。当然,也可以对于 $2^m$ 个串整体进行一次 DP。注意为了无后效性,DP 要倒序做。时间复杂度 $O(2^m \times M)$。

所以,我们当 $m \ge \sqrt{n}$ 的时候用第一种方案,否则用第二种方案即可。总时间复杂度:

(为啥这个题 $n$ 不出到 $400$ 啊 QAQ)

代码实现

1
#include <cstdio>
2
#include <cstring>
3
#include <algorithm>
4
using namespace std;
5
6
const int maxn = 300, maxm = 1 << 18, inf = 2e9 + 1;
7
int n, m;
8
char s[maxn + 3];
9
10
namespace subtask1 {
11
	int k, a[maxn + 3], c[maxn + 3][2], ans = maxn;
12
	void dfs(int d, int sum) {
13
		if (!d) {
14
			for (int i = 1; i <= m; i++) {
15
				sum += min(c[i][0], c[i][1]);
16
			}
17
			ans = min(ans, sum);
18
			return;
19
		}
20
		for (int t = 0; t < 2; t++) {
21
			a[d] = t;
22
			for (int i = 1; i <= m; i++) {
23
				c[i][s[(d - 1) * m + i] - '0' != t]++;
24
			}
25
			dfs(d - 1, sum + (a[d] != a[d + 1]));
26
			for (int i = 1; i <= m; i++) {
27
				c[i][s[(d - 1) * m + i] - '0' != t]--;
28
			}
29
		}
30
	}
31
	void main() {
32
		k = n / m;
33
		for (int i = m * k + 1; i <= n; i++) {
34
			c[i - m * k][s[i] - '0']++;
35
		}
36
		dfs(k, 0);
37
		printf("%d\n", ans);
38
	}
39
}
40
41
namespace subtask2 {
42
	int k, dp[2][maxm + 3][2];
43
	int bit_cnt(int x) {
44
		return __builtin_popcount(x);
45
	}
46
	int diff(int msk1, int msk2) {
47
		return bit_cnt(msk1 ^ msk2);
48
	}
49
	void upd(int &x, int y) {
50
		x > y ? x = y : 0;
51
	}
52
	void main() {
53
		k = n / m;
54
		int bot = 0;
55
		for (int i = m * k + 1; i <= n; i++) {
56
			bot = bot << 1 | (s[i] - '0');
57
		}
58
		int cur = 0, lst = 1;
59
		memset(dp[cur], 0x3c, sizeof(dp[cur]));
60
		for (int msk = 0; msk < 1 << m; msk++) {
61
			dp[cur][msk][0] = diff(bot, msk >> (m - n + m * k));
62
		}
63
		for (int i = k; i; i--) {
64
			swap(cur, lst);
65
			memset(dp[cur], 0x3c, sizeof(dp[cur]));
66
			int num = 0;
67
			for (int j = 1; j <= m; j++) {
68
				num = num << 1 | (s[(i - 1) * m + j] - '0');
69
			}
70
			for (int msk = 0; msk < 1 << m; msk++) {
71
				for (int j = 0; j < 2; j++) {
72
					for (int t = 0; t < 2; t++) {
73
						upd(dp[cur][msk][t], dp[lst][msk][j] + (j ^ t) + diff(num ^ (t ? (1 << m) - 1 : 0), msk));
74
					}
75
				}
76
			}
77
		}
78
		int ans = inf;
79
		for (int msk = 0; msk < 1 << m; msk++) {
80
			ans = min(ans, min(dp[cur][msk][0], dp[cur][msk][1]));
81
		}
82
		printf("%d\n", ans);
83
		return;
84
	}
85
}
86
87
int main() {
88
	scanf("%s %d", s + 1, &m);
89
	n = strlen(s + 1);
90
	if (m > 18) {
91
		subtask1::main();
92
	} else {
93
		subtask2::main();
94
	}
95
	return 0;
96
}