办法总比困难多

137 篇文章 1 订阅
89 篇文章 0 订阅

题目

题目描述
当一切轰然倒塌,当风暴来临时,我听见 木示木干 说:

毛主席说过:只要思想不滑坡,办法总比困难多!

于是我又重新获得了勇气继续上路了。用 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 nTC 的字符串——沐目女未 要问你 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)lxy+1minx1yrsum(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=l1xsum(l,i) 。而总的删除量是 p r p_r pr ,所以 ( x , r ] (x,r] (x,r] 中删去了 p r − p x p_r-p_x prpx 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=l1maxr{sum(i+1,r)(prpi)}

与前面的操作次数 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=l1maxr{sum(i+1,r)pr+pi}i=l1maxr{sum(i+1,r)+j=l1maxisum(l,j)}l1jirmaxsum(l,j)+sum(i+1,r)l1j<ir+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=CT 的版本,所以有些地方 max ⁡ \max max 变成了 min ⁡ \min min ,而 a − b a-b ab 变成了 b − a b-a ba

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值