D. Kirk and a Binary String
题意
给出一个01串S,长度
≤
100000
\leq 100000
≤100000
请你构建一个01串P,长度与S相同
使得对于任意l,r,
1
≤
l
≤
r
≤
S
1\leq l \leq r \leq S
1≤l≤r≤S
对于区间[l,r]的最长不下降子串长度相同
思路
- 由于只有
1变为0,若[l,r]改变,[1,n]必定改变- 我们可以将
S分成若干的01串,且每个串前面都为0,后面都为1 - 比如将
0111001101分成0111,0011,01 [1,n]的LIS即为将每个段中取出0或1[L,R]的LIS求法与[1,n]同,若[L,R]改变,则必定有01分串改变
- 我们可以将
- 取上面的逆否命题,若
[1,n]不变,[l,r]不变
对于区间[l,r],若将位置i的1改为0
新的最长不下降子序列长度为:
设LIS(l,r)为l到r的最长不下降子序列,sum[x][l,r]为l到r中x的个数
-
若
[l,i]均为0,显然可以改变为0,无影响 -
L I S ( l , r ) = m a x ( L I S ( l , i − 1 ) + s u m [ 1 ] [ i + 1 ] [ r ] , s u m [ 0 ] [ i − 1 ] + 1 + L I S ( i + 1 , r ) ) LIS(l,r)=max(LIS(l,i-1)+sum[1][i+1][r],sum[0][i-1]+1+LIS(i+1,r)) LIS(l,r)=max(LIS(l,i−1)+sum[1][i+1][r],sum[0][i−1]+1+LIS(i+1,r))
前面的
LIS加上后面的1或者后面的LIS加上前面的0 -
显然改变后,只改变了
0,1个数,LIS[l][r]不改变
代码
预处理LIS
int cnt = 0;
for (int i = 1; i <= n; pre_dp[i] = cnt, i++) {
if (d[cnt] <= res[i]) d[++cnt] = res[i];
else d[upper_bound(d + 1, d + 1 + cnt, res[i]) - d] = res[i];
}
cnt = 0;
d[cnt] = 2;
for (int i = n; i >= 1; suf_dp[i] = cnt, i--) {
if (d[cnt] >= res[i]) d[++cnt] = res[i];
else d[upper_bound(d + 1, d + 1 + cnt, res[i],greater<int>()) - d] = res[i];
}
solve
int pre_zero = 0, suf_one = 0;
for (int i = 2; i <= n; suf_one += res[i] == 1, i++);
int lis = pre_dp[n];
for (int i = 1; i <= n; i++) {
if (res[i] && lis == max(pre_dp[i - 1] + suf_one,
pre_zero + 1 + suf_dp[i + 1])) {
res[i] = 0;
pre_zero++;
suf_one--;
} else {
pre_zero += res[i] == 0;
suf_one -= res[i] == 1;
}
}
AC
char s[maxn];
int res[maxn];
int pre_dp[maxn], suf_dp[maxn];
int d[maxn];
int main() {
scanf("%s", s + 1);
int n = strlen(s + 1);
for (int i = 1; i <= n; i++) if (s[i] == '1')res[i] = 1;
int cnt = 0;
for (int i = 1; i <= n; pre_dp[i] = cnt, i++) {
if (d[cnt] <= res[i]) d[++cnt] = res[i];
else d[upper_bound(d + 1, d + 1 + cnt, res[i]) - d] = res[i];
}
cnt = 0; d[cnt] = 2;
for (int i = n; i >= 1; suf_dp[i] = cnt, i--) {
if (d[cnt] >= res[i]) d[++cnt] = res[i];
else d[upper_bound(d + 1, d + 1 + cnt, res[i],greater<int>()) - d] = res[i];
}
int pre_zero = 0, suf_one = 0; for (int i = 2; i <= n; suf_one += res[i] == 1, i++);
int lis = pre_dp[n];
for (int i = 1; i <= n; i++) {
if (res[i] && lis == max(pre_dp[i - 1] + suf_one,
pre_zero + 1 + suf_dp[i + 1])) {
res[i] = 0; pre_zero++; suf_one--;
}
else {
pre_zero += res[i] == 0;
suf_one -= res[i] == 1;
}
}
for (int i = 1; i <= n; i++) s[i] = res[i] + '0';
printf("%s\n", s + 1);
}
探讨了在给定01串S下,构建长度相同的01串P,使任意区间[l,r]的最长不下降子串长度一致的算法。通过预处理LIS,采用动态规划策略,确保修改后子序列长度不变。
5886

被折叠的 条评论
为什么被折叠?



