D. Trees and Segments

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 si=0, then the i-th tree in the row should be an oak, and if si=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 l0 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 l0=0.
  • Then, he will calculate l1 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 l1=0.
  • Finally, he will calculate the beauty of the row as al0+l1 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 si 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 (1t1000) — the number of test cases.

The first line of each test case contains two integers n and k (1n3000, 0kn) — 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 (1jn) 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 l0=0 and l1=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{1,2} the teachers can, for example, change the plan s to 0111 (by changing s4), and for a{3,4} — to 0010 (by changing s2). In this case, the beauty of the row for each a is calculated as follows:

  • For a=1: l0=1, l1=3. The beauty of the row is 11+3=4.
  • For a=2: l0=1, l1=3. The beauty of the row is 21+3=5.
  • For a=3: l0=2, l1=1. The beauty of the row is 32+1=7.
  • For a=4: l0=2, l1=1. The beauty of the row is 42+1=9.

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

 

解题思路

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

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

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

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

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

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

  上面做法的伪代码如下:

复制代码
 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(n4)。其中上面代码中找到[1,l1][r+1,n]中连续最长的一段1用到了动态规划。

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

  先定义状态h(i,j)表示前缀中以第i个字符作为连续最长一段1的右端点,且最多修改j次得到的最大长度。状态转移方程为h(i,j)={h(i1,j)+1ifstri=0h(i1,j1)+1ifstri=1andj>00otherwise

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

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

  同理定义状态h(i,j)表示后缀中以第i个字符作为连续最长一段1的左端点,且最多修改j次得到的最大长度。状态转移方程为h(i,j)={h(i+1,j)+1ifstri=0h(i+1,j1)+1ifstri=1andj>00otherwise

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

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

  上面dp的时间复杂度是O(nk)

  AC代码如下,时间复杂度为O(nk+n2)

复制代码
 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:https://codeforces.com/blog/entry/119398

posted @   onlyblues  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示