题解 字符串
将C看成1,T看成-1
暴力的话考虑前后各扫一次,从前面扫的那一次贪心的尽量靠后
然后从后向前扫一遍统计剩下的点的后缀最小值
优化的难点在于统计重复区间
但这样不好优化
发现在贪心策略下我们需要删除的点实际上是前缀和第一次为-1,-2……的位置,
以及删除这些点后后缀和第一次为-1,-2……的位置
于是考虑如何在删除一些点之后维护剩下的点后缀和中的这些位置
发现在一个点后面被删除的点的数量是总应删点数减去在其前面的应删点数
于是可以表示出一个点在新序列中的后缀和
列出式子来可以写成一个 \(-min(pre_i+suf_j)\)
发现这个东西其实是最小的前缀+后缀,也就是总和减去最大子段和
于是线段树维护最大子段和即可
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define ll long long
//#define int long long
int n, q;
char s[N];
int a[N], pre[N], suf[N];
namespace force{
bool vis[N];
void solve() {
scanf("%d", &q);
for (int i=1,l,r; i<=q; ++i) {
scanf("%d%d", &l, &r);
for (int j=l; j<=r; ++j) vis[j]=0;
int pre=0, cnt1=0;
for (int j=l; j<=r; ++j) {
if (a[j]==1) ++pre;
else if (a[j]==-1 && !vis[j]) {
if (pre<1) vis[j]=1, ++cnt1;
else --pre;
}
}
int suf=0, cnt2=0;
for (int j=r; j>=l; --j) {
if (a[j]==1) ++suf;
else if (a[j]==-1 && !vis[j]) {
if (suf<1) vis[j]=1, ++cnt2;
else --suf;
}
}
printf("%d\n", cnt1+cnt2);
}
exit(0);
}
}
namespace task1{
int tem[N];
void solve() {
scanf("%d", &q);
for (int i=1,l,r; i<=q; ++i) {
scanf("%d%d", &l, &r);
int ans=0, t, minn, mini;
t=pre[l-1]; minn=INF;
for (int j=l; j<=r; ++j) tem[j]=pre[j]-t;
// for (int j=l; j<=r; ++j) if (tem[j]<)
if (minn<0) ans=max(ans, abs(minn));
t=suf[r+1]; minn=INF;
for (int j=l; j<=r; ++j) tem[j]=suf[j]-t;
for (int j=l; j<=r; ++j) minn=min(minn, tem[j]);
if (minn<0) ans=max(ans, abs(minn));
printf("%d\n", ans);
}
exit(0);
}
}
namespace task{
int tl[N<<2], tr[N<<2];
struct seg{
int ls, rs, ms, sum;
seg(){}
seg(int a, int b, int c, int d):ls(a),rs(b),ms(c),sum(d){}
inline seg operator + (seg b) {return seg(max(ls, sum+b.ls), max(b.rs, rs+b.sum), max(rs+b.ls, max(ms, b.ms)), sum+b.sum);}
}dat[N<<2];
#define tl(p) tl[p]
#define tr(p) tr[p]
#define dat(p) dat[p]
#define pushup(p) dat(p)=dat(p<<1)+dat(p<<1|1)
void build(int p, int l, int r) {
tl(p)=l; tr(p)=r;
if (l==r) {dat(p)=seg(a[l], a[l], max(a[l], 0), a[l]); return ;}
int mid=(l+r)>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
pushup(p);
}
seg query(int p, int l, int r) {
if (l<=tl(p)&&r>=tr(p)) return dat(p);
int mid=(tl(p)+tr(p))>>1;
if (l<=mid&&r>mid) return query(p<<1, l, r)+query(p<<1|1, l, r);
else if (l<=mid) return query(p<<1, l, r);
else return query(p<<1|1, l, r);
}
void solve() {
build(1, 1, n);
scanf("%d", &q);
seg t;
for (int i=1,l,r; i<=q; ++i) {
scanf("%d%d", &l, &r);
t=query(1, l, r);
printf("%d\n", t.ms-t.sum);
}
exit(0);
}
}
signed main()
{
freopen("d.in", "r", stdin);
freopen("d.out", "w", stdout);
scanf("%d%s", &n, s+1);
for (int i=1; i<=n; ++i) a[i]=s[i]=='C'?1:-1;
for (int i=1; i<=n; ++i) pre[i]=pre[i-1]+a[i];
for (int i=n; i; --i) suf[i]=suf[i+1]+a[i];
// force::solve();
task::solve();
return 0;
}