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);
}