CF653F Paper task

CF653F Paper task

这里如果不要求子串本质不同,那么就是一个简单的计数。一个结论:如果考虑每一次在当前的字符串后面不断地加字符,那么从未出现过的串的右端点是当前的 \(n\) ,左端点一定是一个前缀。我们可以利用 SAM 的在线性求出这一个前缀的右端点,即当前的 \(n-len[link[last]]\) (就是 生成魔咒 这道题的做法)。

求出这一个前缀之后,我们还需要考虑以当前的右端点往左扩展的合法括号串数,把 ( 看成 -1 ,) 看成 1 ,那么这要求后缀和数组的一个后缀全部非负,且当前后缀和是 0 。

我们用线段树维护后缀和数组,每一次线段树上二分出最靠右的后缀和为负的位置,合法的左端点一定在这个点右边。现在合法的左端点是一个区间,只需要数出这个区间里有多少个 0 就是答案了。

区间数数难有单 \(\log\) 做法,考虑一个前刚不久在模拟考时遇到的 trick ,由于当前合法左端点区间的最小值非负,维护最小值与最小值个数,就可以数出 0 的个数了。

Code

#include <cstdio>
using namespace std;
const int N=1000003;
int n,cnt,last;
char s[N];
struct sta{
	int mn,cnt;
	sta(int M=0,int C=0):mn(M),cnt(C){}
	friend sta operator+(const sta x,const sta y){
		if(x.mn<y.mn) return x;
		if(x.mn>y.mn) return y;
		return sta(x.mn,x.cnt+y.cnt);
	}
};
int tr[N][2],len[N],link[N];
void extend(int c){
	int cur=++cnt,p=last;
	len[cur]=len[last]+1;last=cur;
	while(p&&!tr[p][c]) tr[p][c]=cur,p=link[p];
	if(!p) {link[cur]=1;return;}
	int q=tr[p][c];
	if(len[q]==len[p]+1) {link[cur]=q;return;}
	int clone=++cnt;
	link[clone]=link[q];len[clone]=len[p]+1;
	tr[clone][0]=tr[q][0];tr[clone][1]=tr[q][1];
	link[cur]=link[q]=clone;
	while(p&&tr[p][c]==q) tr[p][c]=clone,p=link[p];
}
sta sg[N<<2];
int tg[N<<2];
void build(int p,int l,int r){
	if(l==r) {sg[p]=sta(0,1);return;}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	sg[p]=sg[p<<1]+sg[p<<1|1];
}
void pushdown(int p){
	if(tg[p]){
		sg[p<<1].mn+=tg[p];
		sg[p<<1|1].mn+=tg[p];
		tg[p<<1]+=tg[p];
		tg[p<<1|1]+=tg[p];
		tg[p]=0;
	}
}
sta query(int L,int R,int p,int l,int r){
	if(L>R) return sta();
	if(L<=l&&r<=R) return sg[p];
	int mid=(l+r)>>1;
	pushdown(p);
	if(R<=mid) return query(L,R,p<<1,l,mid);
	if(L>mid) return query(L,R,p<<1|1,mid+1,r);
	return query(L,R,p<<1,l,mid)+query(L,R,p<<1|1,mid+1,r);
}
void modify(int R,int v,int p,int l,int r){
	if(r<=R) {sg[p].mn+=v;tg[p]+=v;return;}
	int mid=(l+r)>>1;
	pushdown(p);
	modify(R,v,p<<1,l,mid);
	if(R>mid) modify(R,v,p<<1|1,mid+1,r);
	sg[p]=sg[p<<1]+sg[p<<1|1];
}
int locate(int p,int l,int r){
	if(sg[p].mn>=0) return 0;
	if(l==r) return l;
	pushdown(p);
	int mid=(l+r)>>1;
   	if(sg[p<<1|1].mn<0) return locate(p<<1|1,mid+1,r);
   	return locate(p<<1,l,mid);
}
int main(){
	scanf("%d",&n);
	scanf("%s",s+1);
	cnt=last=1;build(1,1,n);
	long long res=0;
	for(int i=1;i<=n;++i){
		if(s[i]=='(') extend(0),modify(i,-1,1,1,n);
		else extend(1),modify(i,1,1,1,n);
		sta tmp=query(locate(1,1,n)+1,i-len[link[last]],1,1,n);
		if(!tmp.mn) res+=tmp.cnt;
	}
	printf("%lld\n",res);
	return 0;
}
posted @ 2022-03-16 15:19  yyyyxh  阅读(30)  评论(0编辑  收藏  举报