• [十二省联考2019]字符串问题:后缀数组+主席树优化建图


    分析

    蒟蒻不会(SAM),只好来一发主席树优化建图的题解。

    (N)为原字符串的长度。首先我们考虑一个最基本的思路,我们发现(A_j)能接在(A_i)后面当且仅当存在一个(B_k)(A_i)支配且是(A_j)的前缀。考虑建图,如果(A_i)支配(B_j),那么从(A_i)(B_j)连一条单向边,如果(B_i)(A_j)的前缀,那么从(B_i)(A_j)连一条单向边。所有的(A_i)有权值,权值为这个串(A_i)的长度。那么跑一个最长链就好了,如果有环输出(-1)(根据题意不可能有(0)环)。

    考虑一个(80)分的做法,即保证了(|A_i| geq |B_j|)的部分。我们发现前面所述的做法的连边数量是(O(N^2))级别的,门槛在“如果(B_i)(A_j)的前缀”这一部分的连边。考虑到如果保证了(|A_i| geq |B_j|),那么“(B_i)(A_j)的前缀”这一条件等价于( ext{lcp}(S(lb_i,N),S(la_j,N)) geq |B_j|)。这样转化有什么用呢?如果我们把所有的(A)串按照(S(la,N))进行后缀排序的话,会发现所有满足“(B_i)(A_j)的前缀”的(A_j)是一段连续的区间,那么就可以使用线段树优化建图了。

    考虑满分做法,如果(|A_i| geq |B_j|)这个条件不再满足,那么“(B_i)(A_j)的前缀”这一条件等价于( ext{lcp}(S(lb_i,N),S(la_j,N)) geq |B_j|)(|A_j| geq |B_i|),即和前面相比多了一维的限制,这一维的限制我们可以使用主席树代替线段树处理掉。

    (N,N_a,N_b)同阶,则时间复杂度和空间复杂度均为(O(TN log N))

    点我查看万恶之源

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cctype>
    #include <algorithm>
    #include <utility>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    
    #define rin(i,a,b) for(int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(int i=(a);i>=(b);--i)
    #define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
    #define Size(a) (int)a.size()
    #define pb push_back
    #define lowbit(a) ((a)&(-(a)))
    typedef long long LL;
    
    using std::cerr;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=200005;
    
    int n,m,na,nb,b[MAXN],l[MAXN],pos[MAXN],pos2[MAXN],odr[MAXN];
    int siz,sa[MAXN],rk[MAXN<<1],sc[MAXN<<1],bk[MAXN],ht[MAXN],st[20][MAXN];
    int ecnt,tot,root[MAXN],lc[MAXN*20],rc[MAXN*20],head[MAXN*20],deg[MAXN*20],w[MAXN*20],loc,ql,qr;
    char s[MAXN];
    LL f[MAXN*20];
    std::queue<int> q;
    
    struct node{
    	int x,id,len;
    }a[MAXN];
    
    struct Edge{
    	int to,nxt;
    }e[MAXN*50];
    
    inline void add_edge(int bg,int ed){
    	++ecnt;
    	e[ecnt].to=ed;
    	e[ecnt].nxt=head[bg];
    	head[bg]=ecnt;
    	++deg[ed];
    }
    
    void radix_sort(){
    	rin(i,1,siz)bk[i]=0;
    	rin(i,1,n)++bk[rk[i]];
    	rin(i,1,siz)bk[i]+=bk[i-1];
    	irin(i,n,1)sa[bk[rk[sc[i]]]--]=sc[i];
    }
    
    void suffix_sort(){
    	siz=26;
    	rin(i,1,n)rk[i]=s[i],sc[i]=i;
    	radix_sort();
    	for(int wd=1;;wd<<=1){
    		int cnt=0;
    		rin(i,1,wd)sc[++cnt]=n-wd+i;
    		rin(i,1,n)if(sa[i]-wd>0)sc[++cnt]=sa[i]-wd;
    		radix_sort();
    		std::swap(rk,sc);
    		rk[sa[1]]=cnt=1;
    		rin(i,2,n)rk[sa[i]]=(sc[sa[i-1]]==sc[sa[i]]&&sc[sa[i-1]+wd]==sc[sa[i]+wd]?cnt:++cnt);
    		siz=cnt;
    		if(cnt==n)return;
    	}
    }
    
    inline void calc_height(){
    	int preh=0;
    	rin(i,1,n){
    		if(rk[i]==1){
    			preh=ht[rk[i]]=0;
    			continue;
    		}
    		int now=std::max(preh-1,0);
    		while(s[sa[rk[i]-1]+now]==s[i+now])++now;
    		preh=ht[rk[i]]=now;
    	}
    }
    
    void build_st(){
    	rin(i,1,n)st[0][i]=ht[i];
    	int lim=log2(n);
    	rin(i,1,lim)rin(j,1,n-(1<<i)+1)
    		st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
    }
    
    inline int lcp(int x,int y){
    	if(x==y)return n-x+1;
    	x=rk[x],y=rk[y];
    	if(x>y)std::swap(x,y);
    	++x;int lim=log2(y-x+1);
    	return std::min(st[lim][x],st[lim][y-(1<<lim)+1]);
    }
    
    inline bool cmp(node x,node y){
    	return rk[x.x]<rk[y.x];
    }
    
    inline bool cmp2(int x,int y){
    	return a[x].len>a[y].len;
    }
    
    #define mid ((l+r)>>1)
    
    int ins(int pre,int l,int r){
    	int o=++tot;
    	lc[o]=lc[pre],rc[o]=rc[pre];
    	if(l==r){
    		pos[l]=o;
    		w[o]=a[l].len;
    		return o;
    	}
    	if(loc<=mid)lc[o]=ins(lc[pre],l,mid);
    	else rc[o]=ins(rc[pre],mid+1,r);
    	add_edge(o,lc[o]);
    	add_edge(o,rc[o]);
    	return o;
    }
    
    void conn(int o,int l,int r,int frm){
    	if(ql>qr)return;
    	if(!o)return;
    	if(ql<=l&&r<=qr){
    		add_edge(frm,o);
    		return;
    	}
    	if(mid>=ql)conn(lc[o],l,mid,frm);
    	if(mid<qr)conn(rc[o],mid+1,r,frm);
    }
    
    #undef mid
    
    LL topo(){
    	int cnt=0;LL ret=0;
    	while(!q.empty())q.pop();
    	rin(i,1,tot)if(!deg[i])q.push(i);
    	while(!q.empty()){
    		int x=q.front();q.pop();++cnt;
    		f[x]+=w[x];
    		ret=std::max(ret,f[x]);
    		trav(i,x){
    			int ver=e[i].to;
    			f[ver]=std::max(f[ver],f[x]);
    			--deg[ver];
    			if(!deg[ver])q.push(ver);
    		}
    	}
    	if(cnt<tot)return -1;
    	else return ret;
    }
    
    void clear(){
    	ecnt=tot=0;
    	memset(head,0,sizeof head);
    	memset(deg,0,sizeof deg);
    	memset(w,0,sizeof w);
    	memset(f,0,sizeof f);
    }
    
    int main(){
    	int T=read();
    	while(T--){
    		clear();
    		scanf("%s",s+1);
    		n=strlen(s+1);
    		rin(i,1,n)s[i]-='a'-1;
    		suffix_sort();
    		calc_height();
    		build_st();
    		na=read();
    		rin(i,1,na){
    			int la=read(),ra=read();
    			a[i]=(node){la,i,ra-la+1};
    		}
    		std::sort(a+1,a+na+1,cmp);
    		rin(i,1,na)pos2[a[i].id]=i,odr[i]=i;
    		std::sort(odr+1,odr+na+1,cmp2);
    		root[n+1]=0;int ptr=0;
    		irin(i,n,1){
    			root[i]=root[i+1];
    			while(ptr<na&&a[odr[ptr+1]].len==i){
    				loc=odr[++ptr];
    				root[i]=ins(root[i],1,na);
    			}
    		}
    		nb=read();
    		rin(i,1,nb){
    			int lb=read(),rb=read();
    			b[i]=lb,l[i]=rb-lb+1;
    			w[tot+i]=0;
    		}
    		rin(i,1,nb){
    			int ll=1,rr=std::upper_bound(a+1,a+na+1,(node){b[i],0,0},cmp)-a-1,ret=rr+1;
    			while(ll<=rr){
    				int midd=((ll+rr)>>1);
    				if(lcp(a[midd].x,b[i])>=l[i])ret=midd,rr=midd-1;
    				else ll=midd+1;
    			}
    			ql=ret;
    			ll=std::lower_bound(a+1,a+na+1,(node){b[i],0,0},cmp)-a,rr=na,ret=ll-1;
    			while(ll<=rr){
    				int midd=((ll+rr)>>1);
    				if(lcp(a[midd].x,b[i])>=l[i])ret=midd,ll=midd+1;
    				else rr=midd-1;
    			}
    			qr=ret;
    			conn(root[l[i]],1,na,tot+i);
    		}
    		m=read();
    		rin(i,1,m){
    			int u=read(),v=read();
    			add_edge(pos[pos2[u]],tot+v);
    		}
    		tot+=nb;
    		printf("%lld
    ",topo());
    	}
    	return 0;
    }
    
  • 相关阅读:
    面向对象基础之类与对象
    常用模块(一)
    re模块与正则表达式
    初识模块
    函数进阶篇
    Spring + Mybatis 读写分离
    java包的所有类生成class
    Oralce数据库的优化
    Java 搜索引擎
    JAVA分布式架构的演进
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10668866.html
Copyright © 2020-2023  润新知