【JZOJ 6028】【GDOI2019模拟2019.2.23】字符串

Description

在这里插入图片描述

Solution

先考虑对一个串的子串求不同子串的个数怎么做,

先对询问离线,
考虑不断右移右端点,同时维护左端点为1~i的答案,
显然对于一种相同的子串,只计算最右边的,
在SAM中,一个节点代表了一些子串,而这些子串Right的最大值,也就是Fail树上这个点的所有儿子的Right最大值,
所以每次加入一个点,相当于更改一条链上点的Right最大值,
我发现这个就是LCT上的Access操作,
所以做Access时维护区间加减即可,

复杂度: O ( n log ⁡ 2 ( n ) ) O(n\log^2(n)) O(nlog2(n))

Code

#include <cstdio>
#include <algorithm>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
#define NX(q) ((q)&(-(q)))
#define SD(q) (b[b[q].fa].r==(q))
using namespace std;
typedef long long LL;
const int N=600500;
int read(int &n)
{
	char ch=' ';int q=0,w=1;
	for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
	if(ch=='-')w=-1,ch=getchar();
	for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
int a[N],anx[N];
int ASK[N][3];
LL Ans[N];
int B[N][2],A[N],B0;
int zx[N];
struct SAMT
{
	int a[29];
	int fail,rx,ti;
}b[N];
int b0=1;
int p[N];
bool PX(int q,int w){return b[q].ti<b[w].ti||(b[q].ti==b[w].ti&&b[q].rx<b[w].rx);}
LL sum[N][2];
void ADD(int q,int e){for(int e1=e*(n-q+1LL);q<=n;q+=NX(q))sum[q][0]+=e,sum[q][1]+=e1;}
LL FD(int Q)
{
	LL ans=0,ans1=0;
	for(int q=Q;q;q-=NX(q))ans+=sum[q][0],ans1+=sum[q][1];
	return ans1-(n-Q)*ans;
}
void change(int l1,int r1,int l2){ADD(l1,l2),ADD(r1+1,-l2);}
LL find(int l1,int r1){return FD(r1)-FD(l1-1);}
struct LCTv
{
	struct qqww
	{
		int l,r,fa,Fa,si;
		int miv,mxv,rx;
		int mi,mx;
	}b[N];
	int b0;
	void doit(int q)
	{
		b[q].si=b[b[q].l].si+b[b[q].r].si+1;
		b[q].mi=min(b[q].miv,min(b[b[q].l].mi,b[b[q].r].mi));
		b[q].mx=max(b[q].mxv,max(b[b[q].l].mx,b[b[q].r].mx));
	}
	void rotate(int q)
	{
		int w=b[q].fa;
		swap(b[q].Fa,b[w].Fa);
		swap(b[q].rx,b[w].rx);
		if(SD(q))
		{
			b[w].r=b[q].l;
			b[b[q].l].fa=w;
			b[q].l=w;
		}else
		{
			b[w].l=b[q].r;
			b[b[q].r].fa=w;
			b[q].r=w;
		}
		if(SD(w))b[b[w].fa].r=q;
		else b[b[w].fa].l=q;
		b[q].fa=b[w].fa;b[w].fa=q;
		doit(w),doit(q);
	}
	void Splay(int q,int T)
	{
		for(;b[q].fa!=T;)
		{
			if(b[b[q].fa].fa!=T)
			{
				if(SD(q)==SD(b[q].fa))rotate(b[q].fa);
				else rotate(q);
			}
			rotate(q);
		}
	}
	void access(int q,int rx)
	{
		int w=q;
		for(q=b[q].Fa;q;w=q,q=b[q].Fa)
		{
			Splay(q,0);
			b[b[q].r].rx=b[q].rx;
			b[b[q].r].fa=0,b[b[q].r].Fa=q;
			b[q].r=0;doit(q);
			change(b[q].rx-b[q].mx+1,b[q].rx-(b[q].mi)+1,-1);
			// change(b[q].rx-b[q].mx+1,b[q].rx-b[q].mi+1,-1);
			b[q].rx=rx;b[w].rx=0;
			b[q].r=w;b[w].fa=q;b[w].Fa=0;doit(q);
		}
		change(rx-b[w].mx+1,rx-b[w].mi+1,1);
	}
	int Doit(int mi,int mx,int rx,int fa)
	{
		mi=min(mx,mi);
		b[++b0].miv=mi;b[b0].mxv=mx;
		b[b0].Fa=fa;b[b0].rx=rx;
		doit(b0);
		access(b0,rx);
		return b0;
	}
}LCT;
void link(int q,int w){B[++B0][0]=A[q];A[q]=B0,B[B0][1]=w;}
int nw=1;
void Join_SAM(int c)
{
	++b0;
	b[nw].a[c]=b0;
	b[b0].rx=b[nw].rx+1;
	b[b0].ti=b[nw].ti+1;
	int q,p;
	for(q=b[nw].fail;q&&!b[q].a[c];q=b[q].fail)b[q].a[c]=b0;
	nw=b0;
	p=b[q].a[c];
	if(!q)b[b0].fail=1;
	else if(b[p].rx==b[q].rx+1)b[b0].fail=p;
	else
	{
		++b0;
		b[b0].ti=b[p].ti;
		b[b0].fail=b[p].fail;
		b[nw].fail=b[p].fail=b0;
		b[b0].rx=b[q].rx+1;
		fo(i,0,25)b[b0].a[i]=b[p].a[i];
		for(;q&&b[q].a[c]==p;q=b[q].fail)b[q].a[c]=b0;
	}
}
void Doit(int m)
{
	fo(i,1,b0)p[i]=i;
	sort(p+1,p+1+b0,PX);
	fo(i,1,m)link(ASK[i][1],i);
	int L=2;
	LCT.b[0].mi=1e9;
	fo(i,1,n)
	{
		for(;L<=b0&&b[p[L]].ti<=i;++L)
		{
			int q=p[L];
			// printf("%d\n",q);
			zx[q]=LCT.Doit(b[b[q].fail].rx+1,b[q].rx,b[q].ti,zx[b[q].fail]);
		}
		efo(j,i)
		{
			Ans[B[j][1]]=FD(i)-FD(ASK[B[j][1]][0]-1);
		}
	}
}
int main()
{
	freopen("zifuchuan.in","r",stdin);
	freopen("zifuchuan.out","w",stdout);
	int q,w;
	char ch=getchar();
	for(;ch>='a'&&ch<='z';ch=getchar())a[++n]=ch-'a'+1;
	q=0;
	fo(i,2,n)
	{
		for(;q&&a[q+1]!=a[i];q=anx[q]);
		if(a[q+1]==a[i])++q;
		anx[i]=q;
	}
	w=n-anx[n];
	int la=anx[n];
	for(q=anx[n];q;q=anx[q])
	{
		if(la-anx[q]>w)
		{
			w=0;
			break;
		}
		else if(la-anx[q]==w)la=anx[q];
	}
	if(w&&!la)n=w;
	int n1=n;
	fo(i,n+1,n*3)a[i]=a[i-n];n*=3;
	fo(i,1,n)Join_SAM(a[i]-1);
	read(m);
	fo(i,1,m)
	{
		read(q),read(w);
		if(q>n1)
		{
			int t=(q-1)/n1;
			q-=t*n1,w-=t*n1;
		}
		ASK[i][0]=q,ASK[i][2]=w;
		if(w<=n)ASK[i][1]=w;
		else ASK[i][1]=q+n1*2-1;
	}
	Doit(m);
	fo(i,1,m)
	{
		Ans[i]+=(LL)(n1)*(ASK[i][2]-ASK[i][1]);
		printf("%lld\n",Ans[i]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值