zoukankan      html  css  js  c++  java
  • LG6292 区间本质不同子串个数

    区间本质不同子串个数

    给定一个长度为 (n) 的字符串 (S)(m) 次询问由 (S) 的第 (L) 到第 (R) 个字符组成的字符串包含多少个本质不同的子串。

    定义两个字符串 (a,b) 相同当且仅当 (|a|=|b|) 并且对于 (iin[1,|a|]) 都有 (a_i=b_i)

    对于 (100\%) 的数据,满足 (1leq nleq 10^5)(1leq mleq 2 imes 10^5)(1leq l_ileq r_ileq n(iin[1,m]))

    题解

    https://www.luogu.com.cn/blog/fuyuki/solution-p6292

    对于每个本质不同的字符串 (T) ,假设在前缀 ([1,r]) 中最后一次出现的位置(右端点)为 ( ext{last}_T) ,那么当左端点取到 ([1, ext{last}_T-|T|+1]) 这个区间内的时候, (T) 会对答案产生 (1) 的贡献。

    离线下来,对每个右端点维护所有左端点的答案。

    如果把反串的后缀树建出来,那么将右端点右移一位到达 (r) 就相当于将后缀树上一条到根的路径上的所有字符串的 ( ext{last}) 修改成 (r) 。如果把这个看作 LCT 中的 access 操作,可以发现划分出来的每条链上的 ( ext{last}) 都相同,并且代表的字符串长度连续。

    因此直接用 LCT 进行修改,将链合并的时候就是将一段长度连续的本质不同字符串的 ( ext{last}) 进行修改,这个对答案的影响可以表现成区间加一个公差为 (1) 的等差数列,用线段树维护即可。

    注意到 access 操作的次数是均摊 (O(log n)) ,那么只会进行 (O(nlog n)) 次线段树上的区间修改,复杂度是 (O(nlog^2 n)) 的。而线段树上查询一次的复杂度是 (O(log n)) ,所以总复杂度是 (O(nlog^2 n+mlog n))

    实现中将区间加等差数列单点查询转化成区间加常数区间查询。

    CO int N=2e5+10,inf=1e9;
    namespace SAM{
    	int last=1,tot=1;
    	int ch[N][26],fa[N],len[N],idx[N];
    	
    	void extend(int c,int p){
    		int x=last,cur=last=++tot;
    		len[cur]=len[x]+1,idx[p]=cur;
    		for(;x and !ch[x][c];x=fa[x]) ch[x][c]=cur;
    		if(!x) {fa[cur]=1; return;}
    		int y=ch[x][c];
    		if(len[y]==len[x]+1) {fa[cur]=y; return;}
    		int clone=++tot;
    		copy(ch[y],ch[y]+26,ch[clone]);
    		fa[clone]=fa[y],len[clone]=len[x]+1;
    		fa[cur]=fa[y]=clone;
    		for(;ch[x][c]==y;x=fa[x]) ch[x][c]=clone;
    	}
    }
    
    namespace SEG{
    	int64 sum[4*N],tag[4*N];
    	
    	#define lc (x<<1)
    	#define rc (x<<1|1)
    	#define mid ((l+r)>>1)
    	IN void push_up(int x){
    		sum[x]=sum[lc]+sum[rc];
    	}
    	IN void put_tag(int x,int l,int r,int64 v){
    		sum[x]+=v*(r-l+1),tag[x]+=v;
    	}
    	void push_down(int x,int l,int r){
    		if(tag[x]){
    			put_tag(lc,l,mid,tag[x]),put_tag(rc,mid+1,r,tag[x]);
    			tag[x]=0;
    		}
    	}
    	void modify(int x,int l,int r,int ql,int qr,int64 v){
    		if(ql<=l and r<=qr) return put_tag(x,l,r,v);
    		push_down(x,l,r);
    		if(ql<=mid) modify(lc,l,mid,ql,qr,v);
    		if(qr>mid) modify(rc,mid+1,r,ql,qr,v);
    		push_up(x);
    	}
    	int64 query(int x,int l,int r,int ql,int qr){
    		if(ql<=l and r<=qr) return sum[x];
    		push_down(x,l,r);
    		if(qr<=mid) return query(lc,l,mid,ql,qr);
    		if(ql>mid) return query(rc,mid+1,r,ql,qr);
    		return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
    	}
    	#undef lc
    	#undef rc
    	#undef mid
    }
    
    namespace LCT{
    	int n,ch[N][2],fa[N];
    	int pos[N],tag[N],len[N],low[N];
    	
    	IN bool nroot(int x){
    		return ch[fa[x]][0]==x or ch[fa[x]][1]==x;
    	}
    	IN void push_up(int x){
    		low[x]=min(len[x],min(low[ch[x][0]],low[ch[x][1]]));
    	}
    	IN void put_tag(int x,int p){
    		pos[x]=tag[x]=p;
    	}
    	void push_down(int x){
    		if(tag[x]){
    			put_tag(ch[x][0],tag[x]),put_tag(ch[x][1],tag[x]);
    			tag[x]=0;
    		}
    	}
    	void rotate(int x){
    		int y=fa[x],z=fa[y],l=x==ch[y][1],r=l^1;
    		if(nroot(y)) ch[z][y==ch[z][1]]=x;fa[x]=z;
    		ch[y][l]=ch[x][r],fa[ch[x][r]]=y;
    		ch[x][r]=y,fa[y]=x;
    		push_up(y),push_up(x);
    	}
    	void push_all(int x){
    		if(nroot(x)) push_all(fa[x]);
    		push_down(x);
    	}
    	void splay(int x){
    		push_all(x);
    		for(;nroot(x);rotate(x)){
    			int y=fa[x],z=fa[y];
    			if(nroot(y)) rotate((x==ch[y][1])!=(y==ch[z][1])?x:y);
    		}
    	}
    	void access(int x,int p){
    		for(int i=x,y=0;i;y=i,i=fa[i]){ // edit 1: x will be used later
    			splay(i);
    			if(pos[i]) SEG::modify(1,1,n,pos[i]-SAM::len[i]+1,pos[i]-low[i]+1,-1);
    			ch[i][1]=y;
    			push_up(i);
    		}
    		splay(x);
    		put_tag(x,p);
    		SEG::modify(1,1,n,p-SAM::len[x]+1,p,1);
    	}
    }
    
    char str[N];
    vector<pair<int,int> > qry[N];
    int64 ans[N];
    
    int main(){
    	scanf("%s",str+1);
    	int n=strlen(str+1);
    	for(int i=1;i<=n;++i) SAM::extend(str[i]-'a',i);
    	int m=read<int>();
    	for(int i=1;i<=m;++i){
    		int l=read<int>(),r=read<int>();
    		qry[r].push_back(make_pair(l,i));
    	}
    	LCT::n=n,LCT::low[0]=inf;
    	for(int i=1;i<=SAM::tot;++i){
    		LCT::fa[i]=SAM::fa[i];
    		LCT::len[i]=LCT::low[i]=SAM::len[SAM::fa[i]]+1;
    	}
    	for(int i=1;i<=n;++i){
    		LCT::access(SAM::idx[i],i);
    		for(int j=0;j<(int)qry[i].size();++j)
    			ans[qry[i][j].second]=SEG::query(1,1,n,qry[i][j].first,i);
    	}
    	for(int i=1;i<=m;++i) printf("%lld
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    android studio的lib和jniLibs
    Android Broadcast Receive
    上周热点回顾(7.18-7.24)团队
    上周热点回顾(7.11-7.17)团队
    .NET跨平台之旅:在生产环境中上线第一个运行于Linux上的ASP.NET Core站点团队
    上周热点回顾(7.4-7.10)团队
    上周热点回顾(6.27-7.3)团队
    .NET跨平台之旅:将示例站点升级至ASP.NET Core 1.0团队
    上周热点回顾(6.20-6.26)团队
    上周热点回顾(6.13-6.19)团队
  • 原文地址:https://www.cnblogs.com/autoint/p/13195081.html
Copyright © 2011-2022 走看看