【洛谷 P4384】 [八省联考2018]制胡窜(SAM / 线段树合并)

传送门

首先建出SamSam,线段树合并维护endposendpos
显然考虑endposendpos问题可以变成

给定xx条线段[li,ri][l_i,r_i]
选择两个点切下去,存在某条线段未被切开的方案

首先转化成求所有线段都被切开的方案
C(n1)2C^2_{(n-1)}减去即可
L=r1,R=lxL=r_1,R=l_x

然后分几种情况考虑:
1、存在线段Lli,riRL\le l_i,r_i\le R
那此时答案一定为0

2、L>RL>R
那么此时考虑两种情况
(1):要么两个点分别切开一些线段
(2):要么一个点切开所有线段,另一个点随便放
(1):
考虑一个放在[li,li+1)[l_i,l_{i+1})内,那另一个必须在[R,ri+1)[R,r_{i+1})
那么这个的方案就是
i(li+1li)(ri+1R)\sum_{i}(l_{i+1}-l_i)(r_{i+1}-R)
=i(ri+1ri)(ri+1R)=\sum_{i}(r_{i+1}-r_i)(r_{i+1}-R)
(2):
注意不要与(1)中的重了
所以此时的方案为(LR2)+(LR)(nlen){L-R\choose 2}+(L-R)(n-len)

3、即LRL\le R,且所有其他线段与线段11xx相连
仍然考虑类似2(1)的做法
那么可以得到方案为i(ri+1ri)(ri+1R)\sum_i(r_{i+1}-r_i)(r_{i+1}-R)
注意此时需要满足Rri+1,li+1LR\le r_{i+1},l_{i+1}\le L

考虑还有就是最后一个合法区间[lp+1,L),[l_{p+1},L),
pre,sufpre,suf分别为L+len1L+len-1的前面和后面第一个rr
那么贡献就还有(sufR)(Lper+len1)(suf-R)(L-per+len-1)

用线段树合并的时候维护一下mn,mx,i(ri+1ri),iri+1(ri+1ri)mn,mx,\sum_{i}(r_{i+1}-r_i),\sum_{i}r_{i+1}(r_{i+1}-r_i)

具体实现可以看代码

#include<bits/stdc++.h>
using namespace std;
#define cs const
#define re register
#define pb push_back
#define pii pair<int,int>
#define ll long long
#define fi first
#define se second
#define bg begin
cs int RLEN=1<<20|1;
inline char gc(){
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob)?EOF:*ib++;
}
inline int read(){
    char ch=gc();
    int res=0;bool f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
inline void readstring(char *s){
	int top=0;char ch=gc();
	while(isspace(ch))ch=gc();
	while(!isspace(ch)&&ch!=EOF)s[++top]=ch,ch=gc();
}
template<class tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<class tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=100005;
int n,q;
namespace Seg{
	cs int N=::N*70;
	int mn[N],mx[N],tot,lc[N],rc[N],last;
	ll s1[N],s2[N],tp1,tp2;
	#define mid ((l+r)>>1)
	inline int copy(int r1){
		int u=++tot;mn[u]=mn[r1],mx[u]=mx[r1],lc[u]=lc[r1],rc[u]=rc[r1],s1[u]=s1[r1],s2[u]=s2[r1];
		return u;
	}
	inline void pushup(int u){
		if(!lc[u])mn[u]=mn[rc[u]],mx[u]=mx[rc[u]];
		else if(!rc[u])mn[u]=mn[lc[u]],mx[u]=mx[lc[u]];
		else mn[u]=mn[lc[u]],mx[u]=mx[rc[u]];
		s1[u]=s1[lc[u]]+s1[rc[u]],s2[u]=s2[lc[u]]+s2[rc[u]];
		if(!lc[u]||!rc[u])return;
		int del=mn[rc[u]]-mx[lc[u]];
		s1[u]+=del*mn[rc[u]],s2[u]+=del;
	}
	void insert(int &u,int l,int r,int p){
		u=copy(u);if(l==r){mx[u]=mn[u]=p;return;}
		if(p<=mid)insert(lc[u],l,mid,p);else insert(rc[u],mid+1,r,p);
		pushup(u);
	}
	void merge(int &u,int v){
		if(!u&&!v)return;
		if(!u||!v){u=copy(u+v);return;}
		u=copy(u);
		merge(lc[u],lc[v]),merge(rc[u],rc[v]);
		pushup(u);
	}
	int querymn(int u,int l,int r,int st,int des){
		if(!u||st>des)return 1e9;
		if(st<=l&&r<=des)return mn[u];
		int ret=1e9;
		if(st<=mid)chemn(ret,querymn(lc[u],l,mid,st,des));
		if(ret<1e9)return ret;
		if(mid<des)chemn(ret,querymn(rc[u],mid+1,r,st,des));
		return ret;
	}
	int querymx(int u,int l,int r,int st,int des){
		if(!u||st>des)return -1e9;
		if(st<=l&&r<=des)return mx[u];
		int ret=-1e9;
		if(mid<des)chemx(ret,querymx(rc[u],mid+1,r,st,des));
		if(ret>-1e9)return ret;
		if(st<=mid)chemx(ret,querymx(lc[u],l,mid,st,des));
		return ret;
	}
	void query(int u,int l,int r,int st,int des){
		if(!u)return;
		if(st<=l&&r<=des){
			int del=mn[u]-last;last=mx[u];
			tp1+=s1[u]+1ll*del*mn[u];
			tp2+=s2[u]+del;
			return;
		}
		if(st<=mid)query(lc[u],l,mid,st,des);
		if(mid<des)query(rc[u],mid+1,r,st,des);
	}
	inline int ask(int u,int l,int r,int fir,int k){
		last=fir,tp1=tp2=0,query(u,1,n,l,r);
		return tp1-tp2*k;
	}
	inline int calc(int u,int k){
		return s1[u]-s2[u]*k;
	}
	#undef mid
}
inline ll C2(int x){return 1ll*x*(x-1)/2;}
namespace Sam{
	cs int N=::N<<1;
	int f[18][N],last=1,tot=1;
	int fa[N],nxt[N][10],len[N],pos[N],ps[N];
	inline void insert(int c,int id){
		int cur=++tot,p=last;last=cur;
		len[cur]=len[p]+1,pos[id]=cur,ps[cur]=id;
		for(;p&&!nxt[p][c];p=fa[p])nxt[p][c]=cur;
		if(!p)fa[cur]=1;
		else{
			int q=nxt[p][c];
			if(len[p]+1==len[q])fa[cur]=q;
			else{
				int clo=++tot;
				fa[clo]=fa[q],len[clo]=len[p]+1;
				memcpy(nxt[clo],nxt[q],sizeof(nxt[q]));
				for(;p&&nxt[p][c]==q;p=fa[p])nxt[p][c]=clo;
				fa[cur]=fa[q]=clo;
			}
		}
	}
	int buc[N],rk[N],rt[N];
	inline void build(){
		for(int i=1;i<=tot;i++)buc[len[i]]++,f[0][i]=fa[i];
		for(int i=1;i<=tot;i++)buc[i]+=buc[i-1];
		for(int i=1;i<=tot;i++)rk[buc[len[i]]--]=i;
		for(int i=1;i<=17;i++)
		for(int j=1;j<=tot;j++)f[i][j]=f[i-1][f[i-1][j]];
		for(int i=tot;i>=1;i--){
			int u=rk[i];
			if(ps[u])Seg::insert(rt[u],1,n,ps[u]);
			Seg::merge(rt[fa[u]],rt[u]);
		}
	}
	inline int jump(int u,int k){
		for(int i=17;~i;i--){
			if(len[f[i][u]]>=k)u=f[i][u];
		}
		return u;
	}
	inline ll query(int l,int r){
		int u=pos[r],len=r-l+1;u=jump(u,len);
		int L=Seg::mn[rt[u]],R=Seg::mx[rt[u]]-len+1;
		if(R<=L)return C2(n-1)-(Seg::calc(rt[u],R)+C2(L-R)+1ll*(L-R)*(n-len));
		int pre=Seg::querymx(rt[u],1,n,1,R),suf;
		if(pre-len+1>=L)return C2(n-1);
		ll res=Seg::ask(rt[u],R+1,L+len-1,pre,R);
		pre=Seg::querymx(rt[u],1,n,1,L+len-1),suf=Seg::querymn(rt[u],1,n,L+len,n);
		if(pre!=1e9&&suf!=-1e9&&suf>R)res+=1ll*(suf-R)*(L-pre+len-1);
		return C2(n-1)-res;
	}
}
char s[N];
int main(){
	#ifdef Stargazer
	freopen("lx.in","r",stdin);
	#endif
	n=read(),q=read();
	readstring(s);
	for(int i=1;i<=n;i++)Sam::insert(s[i]-'0',i);
	Sam::build();
	while(q--){
		int l=read(),r=read();
		cout<<Sam::query(l,r)<<'\n';
	}
	return 0;
}
posted @ 2020-02-13 22:53  Stargazer_cykoi  阅读(170)  评论(0编辑  收藏  举报