【SAM+线段树合并】LOJ2479 [九省联考 2018] 制胡窜

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/89321192

【题目】
LOJ
给定一个字符串 S S ,有 Q Q 次询问,每次给出一个字符串 S l , r S_{l,r} ,求有多少对 ( i , j ) ( i < j ) (i,j)(i<j) 满足将 S S 从这里断开后,至少有一段存在 S l , r S_{l,r} 。(有 n 1 n-1 个可行的断点)
n 1 0 5 , Q 3 × 1 0 5 n\leq 10^5,Q\leq 3\times 10^5

【解题思路】
一开始暴力讨论字符串出现位置后发现做不了,于是考虑求每一段都不出现询问串的方案数,那么当然是先合并出 right \text{right} 集合。
先去掉一些特殊情况:

  • 第一个断点不切断任何询问串,那么他一定在询问串第一次出现前,第二个断点取到所有询问串的并,这样就是一个乘积。
  • 第一个断点切断所有询问串,那么它取到所有询问串的并,第二个断点可以取到第一个断点后所有位置,就是一个类似等差数列求和的东西。

假设一共出现 n n 次,剩下要求的东西就是
i = 1 n 1 i × i + 1 n \sum_{i=1}^n 第1次与第i次出现的交 \times 第i+1次与第n次出现的交
注意这里的交是不包含下一个串覆盖的位置的交才能保证不重不漏。而这个东西在通常情况下就是 l i + 1 l i = r i + 1 r i l_{i+1}-l_i=r_{i+1}-r_i ,仅在开头结尾有特例,此时分别对应前两种特殊情况。

那么整理柿子后就可以得到:
a n s = i = 2 n 1 ( r i + 1 r i ) × ( r i + 1 l n 1 + 1 ) = i = 2 n 1 ( r i + 1 r i ) × r i + 1 ( r i + 1 r i ) × ( l n 1 1 ) = ( i = 2 n 1 ( r i + 1 r i ) × r i + 1 ) ( r n r 2 ) × ( l n 1 1 ) \begin{aligned} ans=&\sum_{i=2}^{n-1} (r_{i+1}-r_i)\times (r_{i+1}-l_{n-1}+1)\\ =&\sum_{i=2}^{n-1} (r_{i+1}-r_i)\times r_{i+1}- (r_{i+1}-r_i)\times (l_{n-1}-1)\\ =&(\sum_{i=2}^{n-1} (r_{i+1}-r_i)\times r_{i+1})- (r_{n}-r_2)\times (l_{n-1}-1) \end{aligned}
求和部分实际上是关于相邻两个位置的信息,我们在线段树上额外维护这个信息即可。

复杂度 O ( ( n + Q ) log n ) O((n+Q)\log n) ,细节很多,心态小崩。

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
using namespace std;

typedef long long ll;
const int N=2e5+10,M=N*22,inf=0x3f3f3f3f;

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;

namespace Segment
{
	int rt[N];
	struct node
	{
		ll val;int mi,mx;
		node(ll _v=0,int _i=0,int _x=0):val(_v),mi(_i),mx(_x){}
		node operator +(const node&A){return node(val+A.val+((mx && A.mi)?1ll*A.mi*(A.mi-mx):0),mi?mi:A.mi,A.mx?A.mx:mx);}
	};
	struct Segment
	{
		node t[M];
		int sz,ls[M],rs[M];
		void insert(int &x,int l,int r,int p)
		{
			x=++sz;t[x]=node(0,p,p);
			if(l==r) return;
			int mid=(l+r)>>1;
			if(p<=mid) insert(ls[x],l,mid,p);
			else insert(rs[x],mid+1,r,p);
		}
		int merge(int x,int y)
		{
			if(!x || !y) return x+y;
			int z=++sz;
			ls[z]=merge(ls[x],ls[y]);rs[z]=merge(rs[x],rs[y]);
			t[z]=t[ls[z]]+t[rs[z]];
			return z;
		}
		node query(int x,int l,int r,int L,int R)
		{
			if(!x) return node(0,0,0);
			if(L<=l && r<=R) return t[x];
			int mid=(l+r)>>1;node res=node(0,0,0);
			if(L<=mid) res=res+query(ls[x],l,mid,L,R);
			if(R>mid) res=res+query(rs[x],mid+1,r,L,R);
			return res;
		}
		int querymin(int x,int l,int r,int L,int R)
		{
			if(!x) return inf;
			if(L<=l && r<=R) return t[x].mi;
			int mid=(l+r)>>1,res=inf;
			if(L<=mid) res=min(res,querymin(ls[x],l,mid,L,R));
			if(R>mid) res=min(res,querymin(rs[x],mid+1,r,L,R));
			return res;
		}
		int querymax(int x,int l,int r,int L,int R)
		{
			if(!x) return -inf;
			if(L<=l && r<=R) return t[x].mx;
			int mid=(l+r)>>1,res=-inf;
			if(L<=mid) res=max(res,querymax(ls[x],l,mid,L,R));
			if(R>mid) res=max(res,querymax(rs[x],mid+1,r,L,R));
			return res;
		}
	}T;
}
using namespace Segment;

namespace String
{
	int f[N][20],pos[N];
	vector<int>G[N];
	struct SAM
	{
		int las,sz,fa[N],mx[N],ch[N][10];
		SAM(){las=sz=1;}
		void extend(int x)
		{
			int p,np,q,nq;
			p=las;las=np=++sz;mx[np]=mx[p]+1;
			for(;p && !ch[p][x];p=fa[p]) ch[p][x]=np;
			if(!p) fa[np]=1;
			else
			{
				q=ch[p][x];
				if(mx[q]==mx[p]+1) fa[np]=q;
				else
				{
					nq=++sz;mx[nq]=mx[p]+1;
					memcpy(ch[nq],ch[q],sizeof(ch[q]));
					fa[nq]=fa[q];fa[q]=fa[np]=nq;
					for(;p && ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
				}
			}
		}
		void dfs(int x)
		{
			for(int i=1;i<19;++i) f[x][i]=f[f[x][i-1]][i-1];
			for(auto v:G[x]) dfs(v),rt[x]=T.merge(rt[x],rt[v]);
		}
		void build()
		{
			for(int i=1;i<=sz;++i) G[fa[i]].pb(i),f[i][0]=fa[i];
			dfs(1);
		}
		int get(int l,int r)
		{
			int len=r-l+1;r=pos[r];
			for(int i=18;~i;--i) if(mx[f[r][i]]>=len) r=f[r][i];
			return r;
		}
	}S;
}
using namespace String;

namespace DreamLolita
{
	int n,m;
	char s[N];
	ll calc(int x){return 1ll*x*(x-1)/2;}
	void solution()
	{
		n=read();m=read();scanf("%s",s+1);
		for(int i=1;i<=n;++i) S.extend(s[i]^48),pos[i]=S.las,T.insert(rt[S.las],1,n,i);
		S.build();
		while(m--)
		{
			int l=read(),r=read(),p=rt[S.get(l,r)],len=r-l+1;ll ans=0;
			if(T.t[p].mx-T.t[p].mi<len) ans+=1ll*(len-(T.t[p].mx-T.t[p].mi)-1)*(T.t[p].mi-len+n-T.t[p].mx);
			l=max(T.t[p].mi,T.querymax(p,1,n,1,T.t[p].mx-len+1));
			r=min(T.t[p].mx,T.querymax(p,1,n,1,T.t[p].mi+len-1));
			if(l && r && l<r)
			{
				node res=T.query(p,1,n,l,r);
				ans+=res.val-1ll*(res.mx-res.mi)*(T.t[p].mx-len+1);
			}
			
			if(r==T.t[p].mx) ans+=1ll*(T.t[p].mi-(r-len+1))*((len-2)-(floor(T.t[p].mi-(r-len+1)-1)/2));
			else ans+=1ll*(T.t[p].mi-(r-len+1))*max(T.querymin(p,1,n,r+1,n)-(T.t[p].mx-len+1),0);
			writeln(calc(n-1)-ans);
		}	
	}
}

int main()
{
#ifdef Durant_Lee
	freopen("LOJ2479.in","r",stdin);
	freopen("LOJ2479.out","w",stdout);
#endif
	DreamLolita::solution();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/89321192