[题解] CF1858D Trees and Segments
为什么这个题能有 *2200 啊。。。
题目大意就是
考虑枚举最长
记录
同理,记录
那么当我们枚举了
我们对相同
那么,此时我们就处理出了对于一个区间
需要注意的细节:
-
处理出
ans_0 ; -
判断
ans_i 是否可以取到,若无法取到要直接跳过; -
多测清空。
时间复杂度
Code:
赛时用乱搞的号写的,代码会比较丑。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int t, n, k, a[3010], pre[3010][3010], suf[3010][3010], ans[3010], vis[3010];
char ch[3010];
signed main(){
scanf ("%lld", &t);
while (t --){
scanf ("%lld%lld%s", &n, &k, ch+1);
for (int i=1; i<=n; i++){
a[i] = ch[i] - '0';
ans[i] = vis[i] = 0;
}
a[n+1] = 0;
for (int i=0; i<=k; i++) suf[n+1][i] = 0;
for (int i=1; i<=n; i++){
int now = 0, x = i-1;
for (int j=0; j<=k; j++){//找i右边第j+1个0的位置,没有就是n+1
while (now < j+1 && x <= n){
x ++;
if (a[x] == 0) now ++;
}
suf[i][j] = x - i;
}
}
for (int i=n; i>=1; i--){
for (int j=0; j<=k; j++){
suf[i][j] = max(suf[i][j], suf[i+1][j]);
}
}
for (int i=n; i>=1; i--){
int now = 0, x = i+1;
for (int j=0; j<=k; j++){
while (now < j+1 && x >= 1){
x --;
if (a[x] == 0) now ++;
}
pre[i][j] = i - x;
}
}
for (int i=1; i<=n; i++){
for (int j=0; j<=k; j++){
pre[i][j] = max(pre[i][j], pre[i-1][j]);
}
}
vis[0] = 1;
ans[0] = max(pre[n][k], suf[1][k]);
for (int i=1; i<=n; i++){
int tot = 0;
for (int j=i; j<=n; j++){
if (a[j] == 1) tot ++;
if (tot > k) break;
ans[j-i+1] = max(ans[j-i+1], suf[j+1][k-tot]);
ans[j-i+1] = max(ans[j-i+1], pre[i-1][k-tot]);
vis[j-i+1] = 1;
}
}
for (int i=1; i<=n; i++){
int now = 0;
for (int j=0; j<=n; j++){
if (!vis[j]) continue;
now = max(now, i*j + ans[j]);
}
printf ("%lld ", now);
}
puts ("");
}
return 0;
}