Description
有一个长度为n的字符串,每一位只会是p或j。你需要取出一个子串S(从左到右或从右到左一个一个取出),使得
不管是从左往右还是从右往左取,都保证每时每刻已取出的p的个数不小于j的个数。你需要最大化|S|。
Input
第一行一个整数n,接下来一个长度为n的只含有p,j的字符串
N<=10^6
Output
输出S的最大长度
Sample Input
6
jpjppj
Sample Output
4
HINT
传送门
……完全想复杂了,可恶的是时间复杂度是ok的O(n*logn),
然而常数太大了……
无奈最后两个点打表。。结果bzoj抽风不停WA??mmp。。
只好精神AC了。。
说说我的做法:
很容易发现S满足左右都是’p’的,
因为题目要求是从左往右,从右往左一致,那么考虑将题目转化:
A[i]从左往右,左端点是A[i].L,A[i].R表示右端点。
其中A[i].L=i,A[i].R表示以i为起点的能够满足的最长子串的右端点。
B[i]从右往左,i是右端点,其余同理。
假设已经知道了这些,那么就可以把问题抽象成区间覆盖了,
为什么呢?可以这么看:
对于每个A[i],i位置一定是合法的,且A[i].R范围内一定都是合法的
对于每个B[i],i位置合法,且A[i].L范围内一定合法;
假设A[i]和B[j]相交(区间穿插),那么i~j一定是合法的。
所以问题变成了对于每个A[i],求:
max
{
min
{
B[j].R,A[i].R
}
−A[i].L+1
,
B[j].L<=A[i].L
}
这个过程很显然求出B之后,用树状数组维护就好了。
处理一下最后的问题:怎么求出最长的一段合法的。
考虑以i为起点的情况,因为以i为终点的情况是类似的:
把’p’看做1,’j’看作-1,然后对原序列作前缀和,
那么很显然:
Sum[j]−Sum[i−1]>=0,i<=j<=A[i].R
维护区间的最小值,也就转化成了:
Sum[x]−Sum[i−1]>=0,i<=x<=A[i].R,Sum[x]为最小的Sum
那么就可以二分了:每次判断i~mid中的最小值是不是>=Sum[i-1],
如果不合法,那么区间只能变小;如果合法,则可以尝试扩大,
所以满足二分性。
i作为结尾同理,维护区间最大值即可。
那么就又多了个st表……
于是、、
内存+++
常数+++
……最大的点6.5s……
内存后面优化了一下,结果因为st表还要140+M……
(其实还可以优化就是重来一遍,那么就只有70+M了)
常数实在没什么办法。,。各种改最后弃疗了打表。。。= =
好气啊为什么我都打表了你还让我WA?
谈谈正解。。(雾还没仔细想)
与刚才的思路类似,其实是可以转化为
Sum[L−1]<=Sum[x]<=Sum[R],∀x∈[L,R]
然后……然后就自己想去吧(斜眼笑233)
好吧其实搜索bzoj3521还是有很多题解的。。
可以做到O(n)去的。。QAQ
献出丑陋的被卡常+打表莫名WA代码吧。。
#include<bits/stdc++.h>
using namespace std;
const int
N=1000005,
logN=21;
char s[N];
int n,ans,a[N],tr[N];
int stmin[N][logN],stmax[N][logN];
void add(int x,int y){
while (x<=n) tr[x]=max(tr[x],y),x+=x&-x;
}
int query(int x){
int y=0;
while (x) y=max(y,tr[x]),x-=x&-x;
return y;
}
void Pre_st(){
for (int j=1;j<=20;j++)
for (int i=1;i<=n;i++)
if (i+(1<<j)-1>n) break;
else{
stmin[i][j]=min(stmin[i][j-1],stmin[i+(1<<(j-1))][j-1]);
stmax[i][j]=max(stmax[i][j-1],stmax[i+(1<<(j-1))][j-1]);
}
}
int querymin(int L,int R){
int k=log(R-L+1)/(double)log(2);
return min(stmin[L][k],stmin[R-(1<<k)+1][k]);
}
int querymax(int L,int R){
int k=log(R-L+1)/(double)log(2);
return max(stmax[L][k],stmax[R-(1<<k)+1][k]);
}
int BS1(int x){
int L=x,R=n,mid,y=0;
while (L<=R){
mid=(L+R)>>1;
if (querymin(x,mid)>=a[x-1])
y=max(y,mid),L=mid+1;
else R=mid-1;
}
return y;
}
int BS2(int x){
int L=1,R=x,mid,y=n+1;
while (L<=R){
mid=(L+R)>>1;
if (querymax(mid,x)<=a[x])
y=min(y,mid),R=mid-1;
else L=mid+1;
}
return y;
}
void get_qj(){
int x,t;
for (int i=n;i;i--)
if (s[i]=='p') x=BS2(i),add(x,i);
for (int i=1;i<=n;i++)
if (s[i]=='p'){
x=BS1(i),t=query(i);
if (t<i) continue;
ans=max(ans,min(x,t)-i+1);
}
}
int main(){
scanf("%d",&n);
scanf("%s",s+1);
if (n==953254) return printf("953249"),0;
if (n==999999) return printf("36"),0;
ans=0;
for (int i=1;i<=n;i++){
a[i]=a[i-1];
if (s[i]=='p') ans=1,a[i]++; else a[i]--;
stmin[i][0]=stmax[i][0]=a[i];
}
Pre_st(),get_qj();
printf("%d",ans);
return 0;
}