JZOJ6760 【NOI2020模拟7.22】T3(string)

Description

  • \(n,m≤2*10^5\)

Solution

  • 先考虑\(tp=1\)(即ytp的誊写)。要求在\([1,L-1]∪[R+1,n]\)中出现的本质不同的子串数;那么容斥一下,变成总的本质不同的子串数-每次出现都与\([L,R]\)有交的子串数。前面的问题建一个\(SAM\)可以很好统计;后一个问题,实际上是对于每一个满足\(minr≥L\)的点,统计\(\sum min(R,maxr-len_{fa})-maxr+len\)(其中\(minr\)\(maxr\)分别表示该点的\(right\)集合中的最小、最大值)。
  • 这实际上是个二维数点问题。从后往前扫,扫到一个\(minr\)就在\([maxr-len+1,maxr-len_{fa}]\)上区间+1;扫到一个询问\(L\),就查询\(R\)的前缀和。可以用两个树状数组维护。

  • 再考虑\(tp=2\)。这就多了个求子串的本质不同子串数。(这又是个我完全不造的“经典问题”)
  • 考虑先把整个串的\(SAM\)建出来,记录建到每个位置时的结尾点。从左往右激活每个结尾点,激活时相当于更新它到\(fa\)树祖先这整条链的\(maxr\),并且一个点的\(maxr\)会使\([maxr-len+1,maxr-len_{fa}]\)区间+1;对于一个询问\([L,R]\),我们先把\(≤R\)的结尾点激活,然后区间查询\([L,R]\)的和。
  • 可以直接套\(LCT\),把\(maxr\)相同的放在同一个\(splay\)里;每次修改时直接\(access\),一路打标记、改树状数组。

  • 时间复杂度:\(O(n\log^2n)\)

Code

#include<cstdio>
#include<vector>
#include<cstring>
#define MIN(x,y) if(x>y)x=y
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=4e5+9;
int n,tp,m,l[N],r[N],d[N];
char s[N];
vector<int>e[N],g[N];
ll c[N],c1[N],ans[N];
void mdf(int k,int d) {if(k<=0)k=1; for(int i=k;i<=n;i+=i&-i) c[i]+=d,c1[i]+=d*k;}
ll qry(int k) {ll a=0; for(int i=k;i;i-=i&-i)a+=(k+1)*c[i]-c1[i]; return a;}
struct SAM_LCT
{
	int a[N],u,v,np,cnt=1,ch[N][26],fa[N],Fa[N],len[N],mn[N],mx[N],c[N],_[N],s[N][2],mi[N],tag[N];
	ll ans;
	void ext(char c,int&nw,int la,int i)
	{
		len[nw=++cnt]=len[la]+1;
		for(u=la;u&&!ch[u][c];u=fa[u]) ch[u][c]=nw;
		if(!u) fa[nw]=1; else
		if(len[u]+1==len[v=ch[u][c]]) fa[nw]=v; else 
		{
			len[np=++cnt]=len[u]+1, memcpy(ch[np],ch[v],sizeof ch[np]); mn[np]=mx[np]=mn[v];
			for(;u&&ch[u][c]==v;u=fa[u]) ch[u][c]=np;
			fa[np]=fa[v], fa[v]=fa[nw]=np;
		}
		mn[nw]=mx[nw]=i;
	}
	void cal()
	{
		fo(i,1,cnt) c[len[mi[i]=i]]++, g[mn[i]].push_back(i), ans+=len[i]-len[Fa[i]=fa[i]];
		fo(i,1,n) c[i]+=c[i-1];
		fo(i,1,cnt) _[c[len[i]]--]=i;
		fd(i,cnt,2) if(mx[fa[_[i]]]<mx[_[i]]) mx[fa[_[i]]]=mx[_[i]];
	}
	bool so(int x) {return s[fa[x]][1]==x;}
	bool rt(int x) {return !fa[x]||s[fa[x]][so(x)]!=x;}
	void lk(int f,int x,bool k) {if(f)s[f][k]=x; if(x)fa[x]=f;}
	void up(int x) {mi[x]=s[x][0]?mi[s[x][0]]:x;}
	void rot(int x)
	{
		if(!x) return;
		int y=fa[x],z=fa[y],k=so(x);
		if(rt(y)) fa[x]=z; else lk(z,x,so(y));
		lk(y,s[x][!k],k); lk(x,y,!k);
		up(y); up(x);
	}
	void push(int x) {if(!tag[x])return; if(s[x][0])tag[s[x][0]]=tag[x]; if(s[x][1])tag[s[x][1]]=tag[x];}
	void clr(int x)
	{
		for(d[d[0]=1]=x;!rt(x);d[++d[0]]=x=fa[x]);
		while(d[0]) push(d[d[0]--]);
	}
	void splay(int x)
	{
		clr(x);
		for(int f=fa[x];!rt(x);rot(x),f=fa[x]) rot(rt(f)?0:so(x)==so(f)?f:x);
	}
	void access(int y)
	{
		int x=0,r=mn[y]; mdf(1,1), mdf(r+1,-1);
		for(;y;x=y,y=fa[y])
		{
			int f=y;
			while(!rt(f)) f=fa[f];
			if(tag[f]) mdf(tag[f]-len[y]+1,-1), mdf(tag[f]-len[Fa[mi[f]]]+1,1);
			splay(y); lk(y,x,1); tag[y]=r;
		}
	}
}S;
int main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	scanf("%d%d%s%d",&n,&tp,s+1,&m);
	S.a[0]=1;
	fo(i,1,n) S.ext(s[i]-97,S.a[i],S.a[i-1],i);
	S.cal();
	fo(i,1,m)
	{
		scanf("%d%d",l+i,r+i);
		e[l[i]].push_back(i);
	}
	fd(x,n,1)
	{
		for(int u:g[x]) mdf(S.mx[u]-S.len[u]+1,1), mdf(S.mx[u]-S.len[S.fa[u]]+1,-1);
		for(int i:e[x]) ans[i]=S.ans-qry(r[i])+1ll*r[i]*(n-r[i])+(x-1ll)*(r[i]-x+1ll)+(tp==1?r[i]-x+1:0);
	}
	if(tp==2)
	{
		memset(c,0,8*(n+1)); memset(c1,0,8*(n+1));
		fo(x,1,n) e[x].clear();
		fo(i,1,m) e[r[i]].push_back(i);
		fo(x,1,n)
		{
			S.access(S.a[x]);
			for(int i:e[x]) ans[i]+=qry(x)-qry(l[i]-1);
		}
	}
	fo(i,1,m) printf("%lld\n",ans[i]);
}

猜你喜欢

转载自www.cnblogs.com/Iking123/p/13365664.html
今日推荐