ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[CF653F] Paper Task

2021-04-01 07:33:16  阅读:149  来源: 互联网

标签:ch int len Task link Paper now CF653F define


\(\text{Problem}:\)Paper Task

\(\text{Solution}:\)

合法括号串的性质:左括号个数等于右括号个数;任意一段后缀中,左括号个数小于等于右括号。

( 看作 \(-1\),) 看作 \(1\),那么区间 \([l,r]\) 对应的子串是合法括号串的条件:\(1.\) 区间和为 \(0\);\(2.\) 区间内任意一段后缀和都大于等于 \(0\)。

计算本质不同子串,可以想到 \(\text{SAM}\)。对于状态 \(x\),我们从它对应的 \(\text{endpos}\) 等价类中随便选一个算出答案,然后乘上 \(size_{x}\) 即可。现在问题转化为:求出右端点 \(i\),左端点在区间 \([l,r]\) 内的子串有多少是合法的。

发现当右端点 \(i\) 固定时,左端点越小,越可能不满足条件 \(2\),所以可以二分出满足条件 \(2\) 最小的左端点 \(p\)。区间和为 \(0\) 可以看作前缀和相等,故直接查询区间内有多少数等于 \(x\) 即可。

具体实现:设序列前缀和为 \(a_{i}\),用 \(\text{ST}\) 表维护区间 \(a_{i}\) 最小值得到 \(p\);值域很小,可以 \(vector\) 上二分查找区间有多少数等于 \(x\)。时间复杂度 \(O(n\log n)\)。

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=1000010, M=21;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
char s[N];
int n,qz[N],st[N][M],lg[N]; long long Ans;
vector<int> g[N]; int h[N];
struct SAM
{
	int link[N],len[N],ch[N][2],fir[N];
	int tot,lst;
	inline SAM() { tot=lst=1; link[1]=len[1]=0; memset(ch[1],0,sizeof(ch[1])); }
	inline void Extend(int c)
	{
		int now=++tot;
		int p=lst;
		while(p && !ch[p][c]) ch[p][c]=now, p=link[p];
		len[now]=len[lst]+1;
		fir[now]=len[now];
		if(!p) { lst=now, link[now]=1; return; }
		int q=ch[p][c];
		if(len[q]==len[p]+1) link[now]=q;
		else
		{
			int nq=++tot;
			fir[nq]=fir[q];
			len[nq]=len[p]+1;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
			link[nq]=link[q];
			link[q]=link[now]=nq;
			while(p && ch[p][c]==q) ch[p][c]=nq, p=link[p];
		}
		lst=now;
	}
	inline void Renew()
	{
		for(ri int i=1;i<=tot;i++) link[i]=len[i]=0, memset(ch[i],0,sizeof(ch[i]));
		tot=lst=1;
	}
}A;
inline int Ask(int x,int y)
{
	int k=lg[y-x+1];
	return max(st[x][k],st[y-(1<<k)+1][k]);
}
inline int Find(int l,int r,int p)
{
	int L=l, R=r, tt=-1;
	while(L<=R)
	{
		int mid=(L+R)/2;
		if(qz[p]-Ask(mid-1,p-1)>=0) tt=mid, R=mid-1;
		else L=mid+1;
	}
	return tt;
}
inline int Get(int l,int x)
{
	if(l<0) return 0;
	int L=0, R=h[x]-1, res=0;
	while(L<=R)
	{
		int mid=(L+R)/2;
		if(g[qz[x]+n][mid]<=l) res=mid+1, L=mid+1;
		else R=mid-1;
	}
	return res;
}
signed main()
{
	lg[0]=-1;
	for(ri int i=1;i<N;i++) lg[i]=lg[i>>1]+1;
	n=read();
	scanf("%s",s+1);
	g[n].eb(0);
	for(ri int i=1;i<=n;i++)
	{
		qz[i]=qz[i-1];
		if(s[i]=='(') A.Extend(0), qz[i]--;
		else A.Extend(1), qz[i]++;
		h[i]=(int)g[qz[i]+n].size();
		g[qz[i]+n].eb(i);
	}
	for(ri int i=1;i<=n;i++) st[i][0]=qz[i];
	for(ri int i=1;(1<<i)<=n;i++)
	for(ri int j=0;j+(1<<i)-1<=n;j++)
	st[j][i]=max(st[j][i-1],st[j+(1<<(i-1))][i-1]);
	for(ri int i=2;i<=A.tot;i++)
	{
		int t=A.fir[i];
		int l=t-A.len[i]+1;
		int r=t-A.len[A.link[i]];
		int pos=Find(l,r,t);
		if(pos==-1) continue;
		Ans+=Get(r-1,t)-Get(pos-2,t);
	}
	printf("%lld\n",Ans);
	return 0;
}

标签:ch,int,len,Task,link,Paper,now,CF653F,define
来源: https://www.cnblogs.com/zkdxl/p/14604635.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有