CF2092F Andryusha and CCB 题解

Description

我们定义一个二进制字符串 zz美感值为满足 1i<z1 \le i < |z|zizi+1z_i \neq z_{i+1} 的索引 ii 的数量。

在等待 CCB 的朋友们到来时,Andryusha 烤了一个馅饼,表示为一个长度为 nn 的二进制字符串 ss。为了避免冒犯任何人,他想要将这个字符串分割成 kk 个子字符串,使得每个字符属于恰好一个子字符串,且所有子字符串的美感值相同。

Andryusha 不知道会有多少 CCB 的朋友来他家,因此他希望找出满足条件的所有 kk 值的数量。然而,他的兄弟 Tristan 认为这个问题的表述过于简单。因此,他要求你为字符串的每个前缀找出这样的 kk 值的数量。换句话说,对于每个 ii(从 11nn),你需要找出满足可以将前缀 s1s2sis_1 s_2 \ldots s_i 分割成恰好 kk 个具有相同美感值的子字符串的 kk 值的数量。

1n1061\leq n\leq 10^6

Solution

cntcnt 表示分出来的每个段的美感值,容易发现 cnt=1的个数kcnt=\displaystyle\left\lfloor\frac{1 的个数}{k}\right\rfloor

所以对于一个 kk,其对应的 cntcnt 是唯一的,于是枚举 (k,cnt)(k,cnt),再计算其对哪些前缀造成贡献即可。

由于 k×cntnk\times cnt\leq n,所以这样的数对总数是 O(nlogn)O(n\log n) 级别的。

然后考虑怎么计算其对哪些前缀造成贡献。

先把每个 0101 极长连续段缩起来,设长度为 a1,a2,,ama_1,a_2,\ldots,a_m

如果 k=1k=1,则 iiacnt+1a_{cnt+1} 对应的区间内。如果 k=2k=2,贡献在 a2×cnt+2a2×cnt+2a_{2\times cnt+2}\sim a_{2\times cnt+2}。但是如果 acnt+1>1a_{cnt+1}>1,那么让第一个分界点在 acnt+1a_{cnt+1} 内部就能让 a2×cnt+1a_{2\times cnt+1} 内也能有贡献。

容易发现每个数对的贡献一定是一段区间,不妨设 (k1,cnt)(k-1,cnt) 对应的区间为 alara_l\sim a_r,那么 (k,cnt)(k,cnt) 的右端点为 r+cnt+1r+cnt+1;如果 al>1a_l>1,则左端点为 al+cnta_{l+cnt},否则为 al+cnt+1a_{l+cnt+1}

对于 cnt=0cnt=0 需要单独计算,因为上面那个做法基于必须跨段,而 cnt=0cnt=0 为不能跨段,上面做法就不对了。

时间复杂度:O(nlogn)O(n\log n)

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
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e6 + 5;

int n, m;
int a[kMaxN], ans[kMaxN], pos[kMaxN];
std::string str;

void dickdreamer() {
std::cin >> n >> str;
str = " " + str, m = 0;
std::fill_n(ans + 1, n, 0);
for (int i = 1, lst = 0; i <= n; ++i) {
if (i == n || str[i] != str[i + 1]) {
a[++m] = i - lst;
for (int j = lst + 1; j <= i; ++j) pos[j] = m;
lst = i;
}
}
for (int i = 1; i <= n - 1; ++i) {
int l = i + 1, r = i + 1;
for (; l <= n;) {
++ans[l], --ans[std::min(r, n) + 1];
r += i + 1;
if (a[l] == 1) l += i + 1;
else l += i;
}
}
for (int i = 1; i <= m; ++i) ans[i] += ans[i - 1];
for (int i = 1; i <= n; ++i) {
std::cout << ans[pos[i]] + i - pos[i] + 1 << " \n"[i == 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;
}

CF2092F Andryusha and CCB 题解
https://sobaliuziao.github.io/2025/05/10/post/1309e53b.html
作者
Egg_laying_master
发布于
2025年5月10日
许可协议