题目
题目描述
当一切轰然倒塌,当风暴来临时,我听见 木示木干 说:
毛主席说过:只要思想不滑坡,办法总比困难多!
于是我又重新获得了勇气继续上路了。用
T
T
T 表示困难
(
t
o
u
g
h
t
h
i
n
g
)
(tough\;thing)
(toughthing) ,用
C
C
C 表示办法
(
c
h
a
n
c
e
)
(chance)
(chance) ,那么我们一定要让每一个前缀和后缀都有 T
的数量不多于 C
的数量!
但这还不够。
T
i
m
e
i
s
e
n
d
l
e
s
s
.
Time\;is\;endless.
Timeisendless. 在长度为
n
n
n 的时间轴上——也就是一共有
n
n
n 个 T
与 C
的字符串——沐目女未 要问你
q
q
q 次,有关第
l
l
l 天到第
r
r
r 天——也就是第
l
l
l 个字符到第
r
r
r 个字符形成的字符串——至少要忽略多少天,才能满足上面的条件。忽略一天即去掉某个字符。
数据范围与提示
max
(
n
,
q
)
≤
5
×
1
0
5
\max(n,q)\le 5\times 10^5
max(n,q)≤5×105 。
思路
结论题。你可以猜一下结论。然后我告诉你,结论就是:
设 T T T 为 + 1 +1 +1 而 C C C 为 − 1 -1 −1 那么答案是
s u m ( l , r ) − min l ≤ x ≤ y + 1 x − 1 ≤ y ≤ r s u m ( x , y ) sum(l,r)-\min_{l\le x\le y+1}^{x-1\le y\le r}sum(x,y) sum(l,r)−l≤x≤y+1minx−1≤y≤rsum(x,y)
这里 s u m ( x , y ) sum(x,y) sum(x,y) 表示 ∑ i = x y s i \sum_{i=x}^{y}s_i ∑i=xysi ,即区间求和。
如何得到这个结论?将操作分为两步:让每个前缀合法;让每个后缀合法。
第一次只保证前缀是合法的。显然应该删除比较靠近末尾的 T T T ,这样会更好的帮助后缀的合法化。 [ l , x ] [l,x] [l,x] 中删去 T T T 的数量 p x = max i = l − 1 x s u m ( l , i ) p_x=\max_{i=l-1}^{x}sum(l,i) px=maxi=l−1xsum(l,i) 。而总的删除量是 p r p_r pr ,所以 ( x , r ] (x,r] (x,r] 中删去了 p r − p x p_r-p_x pr−px 个 T T T 。
此时后缀删去 T T T 的数量仍然应当是后缀和的最大值,即
max i = l − 1 r { s u m ( i + 1 , r ) − ( p r − p i ) } \max_{i=l-1}^{r}\{sum(i+1,r)-(p_r-p_i)\} i=l−1maxr{sum(i+1,r)−(pr−pi)}
与前面的操作次数 p r p_r pr 相加就是答案。然后到了喜闻乐见的推式子环节!
p r + max i = l − 1 r { s u m ( i + 1 , r ) − p r + p i } = max i = l − 1 r { s u m ( i + 1 , r ) + max j = l − 1 i s u m ( l , j ) } = max l − 1 ≤ j ≤ i ≤ r s u m ( l , j ) + s u m ( i + 1 , r ) = max l − 1 ≤ j < i ≤ r + 1 s u m ( l , j ) + s u m ( i , r ) \begin{aligned} &p_r+\max_{i=l-1}^{r}\{sum(i+1,r)-p_r+p_i\}\\ =&\max_{i=l-1}^{r}\{sum(i+1,r)+\max_{j=l-1}^{i}sum(l,j)\}\\ =&\max_{l-1\le j\le i\le r}sum(l,j)+sum(i+1,r)\\ =&\max_{l-1\le j<i\le r+1}sum(l,j)+sum(i,r) \end{aligned} ===pr+i=l−1maxr{sum(i+1,r)−pr+pi}i=l−1maxr{sum(i+1,r)+j=l−1maxisum(l,j)}l−1≤j≤i≤rmaxsum(l,j)+sum(i+1,r)l−1≤j<i≤r+1maxsum(l,j)+sum(i,r)
不需要解释吧?第一步是把 p r p_r pr 放进去,抵消掉,然后把 p i p_i pi 展开。第二步合并 max \max max 。第三步更改枚举的值。还差一个第四步:改为用整体减去中间。然后就跟开头的式子等价了。
(其实我总在想,前缀与后缀最大的和,这东西更像构造法……但是我只会推式子。)
区间最小(或者最大)子段和,不是很轻易了吗?哪怕带修改都可以做,何况这是不带修改的。
复杂度 O [ ( n + q ) log n ] \mathcal O[(n+q)\log n] O[(n+q)logn] ,不需要卡常随便过。
代码
我写的是 v a l = C − T val=C-T val=C−T 的版本,所以有些地方 max \max max 变成了 min \min min ,而 a − b a-b a−b 变成了 b − a b-a b−a 。
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxN = 500005;
char s[MaxN]; int n;
namespace STree{
struct Node{
int ls, rs, all, sum;
Node operator + (const Node &t) const {
Node res; res.sum = sum+t.sum;
res.ls = max(ls,sum+t.ls);
res.rs = max(t.rs,t.sum+rs);
res.all = max(max(all,rs+t.ls),t.all);
return res; // easy work
}
};
Node node[MaxN<<2];
# define LSON o<<1,l,(l+r)>>1
# define RSON o<<1|1,(l+r)/2+1,r
void build(int o=1,int l=1,int r=n){
if(l == r){
if(s[l] == 'T')
node[o].sum = -1;
else node[o].sum = 1;
node[o].ls = node[o].rs =
node[o].all = max(0,
node[o].sum);
return ;
}
build(LSON), build(RSON);
node[o] = node[o<<1]+node[o<<1|1];
}
Node query(int ql,int qr,int o,int l,int r){
if(ql <= l && r <= qr) return node[o];
if(qr <= (l+r)/2) return query(ql,qr,LSON);
if((l+r)>>1 < ql) return query(ql,qr,RSON);
return query(ql,qr,LSON)+query(ql,qr,RSON);
}
int query(int l,int r){
return query(l,r,1,1,n).all;
}
}
int zxy[MaxN];
int main(){
n = readint(); scanf("%s",s+1);
STree::build();
for(int i=1; i<=n; ++i){
zxy[i] = zxy[i-1];
if(s[i] == 'T')
-- zxy[i];
else ++ zxy[i];
}
int q = readint();
for(int l,r; q; --q){
l = readint(), r = readint();
printf("%d\n",STree::query(l,r)
- (zxy[r]-zxy[l-1]));
}
return 0;
}