1 条题解

  • 0
    @ 2026-9-24 17:37:03

    前置知识:前缀和,单调栈,线段树上二分。

    对原序列进行一个处理,将 p 当成 11,j 当成 −1-1,进行前缀和,设前缀和数组为 aia_i。

    对于一个满足题目条件的区间 [l,r][l,r],ll、rr 显然满足:

    • al−1≤min⁡i=lraia_{l-1} \le \min_{i=l}^{r} a_i

    • ar≥max⁡i=lraia_{r} \ge \max_{i=l}^{r} a_i

    扫一遍 aa 来枚举 l−1l-1,找出最大的 rr,满足上述两个限制,即 al−1a_{l-1} 小于等于区间 [l−1,r][l-1,r] 最小值,ara_{r} 为区间 [l,r][l,r] 最大值。

    设当前扫到的位置是 ii。考虑第一个限制怎么满足。设满足该限制的最远的点下标为 limlim,发现 limlim 具有单调性(若 limlim 满足该限制,则 lim−1lim-1 也一定满足该限制),可以使用线段树上二分。

    线段树上二分大概的具体过程:

    维护一个支持区间求 minmin 的线段树,所有点初始值为 infinf。

    修改正常写。查询的时候,设当前线段树节点为 pp,当前节点的左儿子为 lsls,当前节点的右儿子为 rsrs,区间维护的最小值为 mnmn,查询的值为 valval。

    当 val≤tr[ls].mnval \le tr[ls].mn:说明 valval 是 lsls 维护区间的最小值了,故递归查询 rsrs;

    否则,说明 valval 已经不是 lsls 维护区间的最小值,右端点不在 lsls 维护的区间内,递归查询 lsls。

    扫完一个点后,将它更新到线段树中。

    因为所有点的初始值是 infinf,所以保证了查询的时候 ii 之前的区间不对查询造成影响,最小只会查询到 ii。

    发现查询出的 rr 不一定满足第二个限制。怎么办?

    考虑在扫的过程中,用单调栈维护满足第二个限制的 rr。在扫到 ii 的时候,在单调栈中二分找出 ≤lim\le lim 的最大的 rr。以 ii 为 l−1l-1 的最长符合题目条件的区间即为 [i+1,r][i+1,r]。扫的过程中,ansans 不断与算出的区间长度取 maxmax 即可。

    注意 l=1l=1 的情况也要计算。

    时间复杂度为 O(nlog⁡n)O(n \log n),瓶颈在线段树上二分。

    AC code:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1000004;
    int n;
    int a[maxn];
    namespace Segtree{
    	struct node{
    		int l,r;
    		int mn;
    	}tr[maxn<<2];
    	#define ls (p<<1)
    	#define rs (p<<1|1)
    	#define mid (tr[p].l+tr[p].r>>1)
    	void build(int p,int l,int r){
    		tr[p].l=l,tr[p].r=r;
    		tr[p].mn=maxn;
    		if(l==r) return;
    		build(ls,l,mid);build(rs,mid+1,r);
    	}
    	void upd(int p,int loc,int val){
    		tr[p].mn=min(tr[p].mn,val);
    		if(tr[p].l==tr[p].r) return;
    		if(loc<=mid) upd(ls,loc,val);
    		else upd(rs,loc,val);
    	}
    	int qry(int p,int val){//到最远哪个点还是min 
    		if(tr[p].l==tr[p].r){
    			if(tr[p].mn<val) return tr[p].l-1;//到单个点的时候val仍然不是最小值,返回原区间-1
    			return tr[p].l;
    		}
    		if(val>tr[ls].mn) return qry(ls,val);
    		else return qry(rs,val);
    	}
    }using namespace Segtree;
    int stk[maxn],tp;
    int bs(int x){
    	int l=1,r=tp+1,md;
    	stk[r]=0;
    	while(l<r){
    		md=l+r>>1;
    		if(stk[md]<=x) r=md;
    		else l=md+1;
    	}
    	return stk[l];
    }//单调栈中二分
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0);
    	cin>>n;
    	char c;
    	for(int i=1;i<=n;i++){
    		cin>>c;
    		if(c=='p') a[i]=1;
    		else a[i]=-1;
    		a[i]+=a[i-1];
    	}
    	build(1,1,n);
    	a[0]=maxn+528;//确保弹栈时不会弹空栈
    	int ans=0;
    	for(int i=n;i>=1;i--){
    		int to=qry(1,a[i]);//lim
    		to=bs(to);//满足条件的最大r
    		if(!to) to=i;
    		ans=max(ans,to-i);//求答案
    		while(a[i]>a[stk[tp]]) tp--;
    		stk[++tp]=i;//维护单调栈
    		upd(1,i,a[i]);//更新线段树
    	}
    	int to=qry(1,0);
    	to=bs(to);//特别处理区间为[1,r]的情况
    	ans=max(ans,to);
    	cout<<ans<<'\n';
    	return 0;
    }
    
    • 1

    信息

    ID
    5186
    时间
    1000ms
    内存
    228MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者