D. Trees and Segments

发布时间 2023-08-16 21:00:55作者: onlyblues

D. Trees and Segments

The teachers of the Summer Informatics School decided to plant $n$ trees in a row, and it was decided to plant only oaks and firs. To do this, they made a plan, which can be represented as a binary string $s$ of length $n$. If $s_i = 0$, then the $i$-th tree in the row should be an oak, and if $s_i = 1$, then the $i$-th tree in the row should be a fir.

The day of tree planting is tomorrow, and the day after tomorrow an inspector will come to the School. The inspector loves nature very much, and he will evaluate the beauty of the row as follows:

  • First, he will calculate $l_0$ as the maximum number of consecutive oaks in the row (the maximum substring consisting of zeros in the plan $s$). If there are no oaks in the row, then $l_0 = 0$.
  • Then, he will calculate $l_1$ as the maximum number of consecutive firs in the row (the maximum substring consisting of ones in the plan $s$). If there are no firs in the row, then $l_1 = 0$.
  • Finally, he will calculate the beauty of the row as $a \cdot l_0 + l_1$ for some $a$ — the inspector's favourite number.

The teachers know the value of the parameter $a$, but for security reasons they cannot tell it to you. They only told you that $a$ is an integer from $1$ to $n$.

Since the trees have not yet been planted, the teachers decided to change the type of no more than $k$ trees to the opposite (i.e., change $s_i$ from $0$ to $1$ or from $1$ to $0$ in the plan) in order to maximize the beauty of the row of trees according to the inspector.

For each integer $j$ from $1$ to $n$ answer the following question independently:

  • What is the maximum beauty of the row of trees that the teachers can achieve by changing the type of no more than $k$ trees if the inspector's favourite number $a$ is equal to $j$?

Input

The first line contains a single integer $t$ ($1 \le t \le 1000$) — the number of test cases.

The first line of each test case contains two integers $n$ and $k$ ($1 \le n \le 3000$, $0 \le k \le n$) — the number of trees in the row and the maximum number of changes.

The second line contains a string $s$ of length $n$, consisting of zeros and ones — the plan description.

It is guaranteed that the sum of all $n$ values for all test cases does not exceed $3000$.

Output

For each test case, print $n$ integers, the $j$-th ($1 \le j \le n$) of which is the maximum beauty of the row of trees after no more than $k$ changes if $a = j$ is used to calculate the beauty.

Example

input

5
3 0
111
4 1
0110
5 0
10000
6 2
101101
7 1
0001101

output

3 3 3 
4 5 7 9 
5 9 13 17 21 
6 9 13 17 21 25 
7 10 13 17 21 25 29 

Note

In the first test case no changes are allowed, so $l_0 = 0$ and $l_1 = 3$ always hold. Thus, regardless of the value of $a$, the beauty of the row of trees will be $3$.

In the second test case for $a \in \{1, 2\}$ the teachers can, for example, change the plan $s$ to $0111$ (by changing $s_4$), and for $a \in \{3, 4\}$ — to $0010$ (by changing $s_2$). In this case, the beauty of the row for each $a$ is calculated as follows:

  • For $a = 1$: $l_0 = 1$, $l_1 = 3$. The beauty of the row is $1\cdot 1 + 3 = 4$.
  • For $a = 2$: $l_0 = 1$, $l_1 = 3$. The beauty of the row is $2\cdot 1 + 3 = 5$.
  • For $a = 3$: $l_0 = 2$, $l_1 = 1$. The beauty of the row is $3\cdot 2 + 1 = 7$.
  • For $a = 4$: $l_0 = 2$, $l_1 = 1$. The beauty of the row is $4\cdot 2 + 1 = 9$.

It can be shown that the changes described above are optimal for all $a$ from $1$ to $4$.

 

解题思路

  题解有点看不懂,大概说一下我的做法吧。

  现在要求的是对于每个$a \in [1,n]$,$a \cdot l_0 + l_1$的最大值是多少,这取决于$l_0$和$l_1$分别是多少。可以知道当对字符串修改后,连续最长的一段$0$,和连续最长的一段$1$分别在左部分和右部分(或者是右部分和左部分),并且没有交集。

  而最长一段$0$的长度$l_0 \in [0,n]$,所以想到枚举最长一段$0$所有可能的长度,当$l_0$固定后枚举找到$l_1$最大能取多少。这样就能在修改之后连续最长的一段$0$是$l_0$的所有方案中,确定$l_1$的最大值,进而使得$a \cdot l_0 + l_1$最大(对于固定的$a$和$l_0$)。同理,最长一段$1$的长度$l_1 \in [0,n]$,同样枚举最长一段$1$所有可能的长度,当$l_1$固定后枚举找到$l_0$最大能取多少。

  最终就会得到最多$2n$个二元组$(l_0,l_1)$,这些二元组涵盖了所有能使得$a \cdot l_0 + l_1$取最大值的情况,接下来我们只需要枚举所有的$a$,固定$a$的值后再枚举所有的二元组,计算$a \cdot l_0 + l_1$取最大值,这一步的时间复杂度是$O(n^2)$。

  上面就是大致的思路,下面给出具体做法,这里只给出枚举$l_0$分别找到最大$l_1$的方法,另外的枚举$l_1$分别找到最大$l_0$的做法也是类似的,只需把字符串中的$0$变成$1$,$1$变成$0$,然后用同样的方法即可。

  先枚举最长一段$0$的长度$\text{len}$,然后在字符串中找到所有长度为$\text{len}$的连续子串,来作为最长的一段$0$。因此需要先判定能否将该子串全部变成$0$,即子串中$1$的个数$\text{cnt}$是否不超过$k$。如果$\text{cnt} > k$那么我们就无法通过不超过$k$步的修改使得整个子串变成$0$。否则$\text{cnt} \leq k$,假设子串的左右端点为$l$和$r$,满足$r-l+1=\text{len}$,那么我们就看看字符串$[1,l-1]$及$[r+1,n]$中连续最长的$1$是多少,记作$t$。最后枚举完所有长度为$\text{len}$的子串,把最大的那个$t$作为$l_1$。

  上面做法的伪代码如下:

 1 for (int len = 0; len <= n; len++) {    // 枚举连续最长的0的长度
 2     int l1 = -1;
 3     for (int l = 1; l + len - 1 <= n; l++) {    // 枚举所有长度为len的子串左端点l
 4         int r = l + len - 1;    // 子串右端点r
 5         int cnt = 0;
 6         for (int i = l; i <= r; i++) {
 7             if (str[i] == '1') cnt++;    // 统计子串中1的个数
 8         }
 9         if (cnt > k) continue;    // 1的个数超过k,无法把子串修改为全0,枚举下一个子串
10         int s = k = cnt;    // 剩余能修改的次数
11         int t = 0;
12         // 找到[1,l-1]中连续最长的一段1
13         for (int i = 1; i < l; i++) {
14             for (int j = 0; j <= s; j++) {
15                 if (str[i] == '1') f[i][j] = f[i - 1][j] + 1;
16                 else if (j) f[i][j] = f[i - 1][j - 1] + 1;
17                 else f[i][j] = 0;
18             }
19             t = max(t, f[i][s]);
20         }
21         // 找到[r+1,n]中连续最长的一段1
22         for (int i = n; i > r; i--) {
23             for (int j = 0; j <= s; j++) {
24                 if (str[i] == '1') g[i][j] = g[i + 1][j] + 1;
25                 else if (j) g[i][j] = g[i + 1][j - 1] + 1;
26                 else g[i][j] = 0;
27             }
28             t = max(t, f[i][s]);
29         }
30         l1 = max(l1, t);
31     }
32     if (l1 != -1) p.push_back({len, l1});    // l1==-1说明字符串中不存在长度为len的连续一段0
33 }

  可以看到时间复杂度是$O(n^4)$。其中上面代码中找到$[1,l-1]$和$[r+1,n]$中连续最长的一段$1$用到了动态规划。

  实际上上面的做法是可以通过预处理优化到$O(n^2)$的,首先求子串中$1$的个数可以用前缀和优化到$O(1)$,找到$[1,l-1]$和$[r+1,n]$中连续最长的一段$1$也可以通过动态规划预处理所有的情况来优化到$O(1)$。

  先定义状态$h(i,j)$表示前缀中以第$i$个字符作为连续最长一段$1$的右端点,且最多修改$j$次得到的最大长度。状态转移方程为$$h(i,j) = \left\{ \begin{array}{c}  h(i-1, j) + 1 & \text{if} & \text{str}_i = 0 \\ h(i-1, j-1) + 1 & \text{if} & \text{str}_i = 1 & \text{and} & j > 0 \\ 0 & \text{otherwise} \end{array}\right.$$

  再定义状态$f(i,j)$表示只考虑前$i$个字符,且最多修改$j$次得到的所有方案中,连续一段$1$的最大长度。根据第$i$个字符是否在最长的连续一段$1$中进行状态划分,因此状态转移方程就是$$f(i,j) = \max\left\{ f(i-1,j), h(i,j) \right\}$$

  这样对于任意的前缀$[1,l]$以及最多能修改的次数$k'$,就能通过查询$f(l,k')$来得到。

  同理定义状态$h'(i,j)$表示后缀中以第$i$个字符作为连续最长一段$1$的左端点,且最多修改$j$次得到的最大长度。状态转移方程为$$h'(i,j) = \left\{ \begin{array}{c} h'(i+1, j) + 1 & \text{if} & \text{str}_i = 0 \\ h'(i+1, j-1) + 1 & \text{if} & \text{str}_i = 1 & \text{and} & j > 0 \\ 0 & \text{otherwise} \end{array}\right.$$

  再定义状态$g(i,j)$表示只考虑$[i,n]$中的字符,且最多修改$j$次得到的所有方案中,连续一段$1$的最大长度。根据第$i$个字符是否在最长的连续一段$1$中进行状态划分,因此状态转移方程就是$$g(i,j) = \max\left\{ g(i+1,j), h'(i,j) \right\}$$

  这样对于任意的前缀$[r,n]$以及最多能修改的次数$k'$,就能通过查询$g(r,k')$来得到。

  上面dp的时间复杂度是$O(n \cdot k)$。

  AC代码如下,时间复杂度为$O(n \cdot k + n^2)$:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 3010;
 5 
 6 int n, m;
 7 char str[N];
 8 int s[N];
 9 int f[N][N], g[N][N], h[N][N];
10 vector<vector<int>> p;
11 
12 void get(int op) {
13     if (op) {
14         for (int i = 1; i <= n; i++) {
15             str[i] ^= 1;
16         }
17     }
18     for (int i = 1; i <= n; i++) {
19         s[i] = s[i - 1];
20         if (str[i] == '1') s[i]++;
21     }
22     for (int i = 0; i <= n + 1; i++) {
23         for (int j = 0; j <= m; j++) {
24             f[i][j] = g[i][j] = h[i][j] = 0;
25         }
26     }
27     for (int i = 1; i <= n; i++) {
28         for (int j = 0; j <= m; j++) {
29             if (str[i] == '1') h[i][j] = h[i - 1][j] + 1;
30             else if (j) h[i][j] = h[i - 1][j - 1] + 1;
31             else h[i][j] = 0;
32             f[i][j] = max(f[i - 1][j], h[i][j]);
33         }
34     }
35     for (int i = n; i; i--) {
36         for (int j = 0; j <= m; j++) {
37             if (str[i] == '1') h[i][j] = h[i + 1][j] + 1;
38             else if (j) h[i][j] = h[i + 1][j - 1] + 1;
39             else h[i][j] = 0;
40             g[i][j] = max(g[i + 1][j], h[i][j]);
41         }
42     }
43     for (int len = 0; len <= n; len++) {
44         int t = -1;
45         for (int i = 1; i + len - 1 <= n; i++) {
46             int j = i + len - 1, cnt = s[j] - s[i - 1];
47             if (cnt <= m) t = max({t, f[i - 1][m - cnt], g[j + 1][m - cnt]});
48         }
49         if (t == -1) break;
50         if (!op) p.push_back({len, t});
51         else p.push_back({t, len});
52     }
53 }
54 
55 void solve() {
56     scanf("%d %d %s", &n, &m, str + 1);
57     p.clear();
58     get(0), get(1);
59     for (int i = 1; i <= n; i++) {
60         int ret = 0;
61         for (auto &q : p) {
62             ret = max(ret, i * q[0] + q[1]);
63         }
64         printf("%d ", ret);
65     }
66     printf("\n");
67 }
68 
69 int main() {
70     int t;
71     scanf("%d", &t);
72     while (t--) {
73         solve();
74     }
75     
76     return 0;
77 }

 

参考资料

  Codeforces Round #893 (Div. 2) Editorial:Codeforces Round #893 (Div. 2) Editorial - Codeforces