zoukankan      html  css  js  c++  java
  • 【BZOJ3879】SvT(后缀数组+单调栈)

    点此看题面

    大致题意: 给定一个字符串,每次询问给出若干后缀,求两两之间的(LCP)之和。

    前言

    明明是想练后缀自动机的,结果看到这题一眼想到后缀数组,而且一两分钟就推出了解法,因此最终默默地写了后缀数组。。。

    不过真的好坑啊,题目最下面写了可能存在相同后缀,且相同后缀只计算一次答案。没看到这句话的我白白浪费了半个小时。

    (LCP)的本质

    考虑后缀数组中我们存下了每一个后缀,而这里的(LCP)正是指某两个后缀的最长前缀,与题目中的意思一致。

    回忆一下我们是如何计算(LCP),然后就会发现,是依靠(RMQ)求出一段区间内(Height)的最小值。

    这也正是后缀数组求(LCP)的本质。

    单调栈

    考虑这一题,我们按照后缀排序的排名枚举每一个后缀。

    对于一个后缀,我们只计算排名在它之前的后缀,然后发现它们的(LCP)就是一段段右端点相同的区间的最小值之和。

    那么只要开一个单调递增的单调栈维护每一段最小值,就可以维护出答案了。

    每次只需要加入(LCP(a_{i-1},a_i))更新栈即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 500000
    #define LN 20
    #define LL long long
    #define X 23333333333333333
    using namespace std;
    int n,m,a[N+5],Sp[N+5],Sv[N+5];char s[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		I void reads(CI n,char *s) {W(isspace(c=tc()));for(RI i=1;i<=n;++i) s[i]=c,c=tc();}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    class SuffixArray//后缀数组
    {
    	private:
    		int p[N+5],rk[N+5],t[N+5],SA[N+5],H[N+5],Lg[N+5],Mn[N+5][LN+5];
    		I void Sort(CI S)//基数排序
    		{
    			RI i;for(i=0;i<=S;++i) t[i]=0;for(i=1;i<=n;++i) ++t[rk[i]];
    			for(i=1;i<=S;++i) t[i]+=t[i-1];for(i=n;i;--i) SA[t[rk[p[i]]]--]=p[i];
    		}
    		I void GetSA(char *s)//求出SA
    		{
    			RI i;for(i=1;i<=n;++i) rk[p[i]=i]=s[i];
    			RI k,t=0,S='z';for(Sort(S),k=1;t^n;S=t,k<<=1)
    			{
    				for(t=0,i=1;i<=k;++i) p[++t]=n-k+i;
    				for(i=1;i<=n;++i) SA[i]>k&&(p[++t]=SA[i]-k);
    				for(Sort(S),i=1;i<=n;++i) p[i]=rk[i];
    				for(rk[SA[1]]=t=1,i=2;i<=n;++i) rk[SA[i]]=
    					(p[SA[i-1]]^p[SA[i]]||p[SA[i-1]+k]^p[SA[i]+k])?++t:t;
    			}
    		}
    		I void GetH(char *s)//求出Height数组
    		{
    			RI i,j,k=0;for(i=1;i<=n;++i) p[SA[i]]=i;
    			for(i=1;i<=n;++i)
    			{
    				if(k&&--k,p[i]==1) continue;j=SA[p[i]-1];
    				W(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;Mn[p[i]][0]=H[p[i]]=k;
    			}
    			for(Lg[0]=-1,i=1;i<=n;++i) Lg[i]=Lg[i>>1]+1;//RMQ预处理
    			for(j=1;j<=Lg[n];++j) for(i=1;i+(1<<j)-1<=n;++i)
    				Mn[i][j]=min(Mn[i][j-1],Mn[i+(1<<j-1)][j-1]);
    		}
    	public:
    		I int operator [] (CI x) Con {return p[x];}
    		I void Init(char *s) {GetSA(s),GetH(s);}//初始化
    		I int LCP(CI a,CI b)//其实这里就是利用其计算方式,求一段区间Heigth的min
    		{
    			RI x=min(p[a],p[b])+1,y=max(p[a],p[b]),k=Lg[y-x+1];
    			return min(Mn[x][k],Mn[y-(1<<k)+1][k]);
    		}
    }S;
    I bool cmp(CI x,CI y) {return S[x]<S[y];}//按后缀排名排序
    int main()
    {
    	RI Qt,i,t,T;LL res,ans;F.read(n),F.read(Qt),F.reads(n,s),S.Init(s);
    	Sp[0]=1;W(Qt--)
    	{
    		for(F.read(m),i=1;i<=m;++i) F.read(a[i]);
    		if(sort(a+1,a+m+1,cmp),m=unique(a+1,a+m+1)-a-1,m==1) {F.writeln(0);continue;}//注意去重emmm
    		for(res=ans=Sv[T=1]=S.LCP(a[1],a[2]),Sp[1]=2,i=3;i<=m;++i)//枚举右端点
    		{
    			#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
    			#define Dec(x,y) ((x-=(y))<0&&(x+=X))
    			t=S.LCP(a[i-1],a[i]);W(T&&Sv[T]>=t) Dec(res,1LL*Sv[T]*(Sp[T]-Sp[T-1])),--T;//维护栈的单调性,弹出元素时要更新答案
    			Sv[++T]=t,Sp[T]=i,Inc(res,1LL*Sv[T]*(Sp[T]-Sp[T-1])),Inc(ans,res);//把LCP加入栈中,累加答案
    		}F.writeln(ans);
    	}return F.clear(),0;
    }
    
  • 相关阅读:
    C/C++解题常用STL大礼包 含vector,map,set,queue(含优先队列) ,stack的常用用法
    PAT甲级1018留坑——第一个测试点未过(Dijikstar+Dfs)
    PAT甲级1019水题飘过
    微信该公众号提供的服务出现故障
    The valid characters are defined in RFC 7230 and RFC 3986
    eclipse中Web Deployment Assembly与build path作用
    Invalid bound statement (not found)
    springmvc获取bean
    mac/linux查询网络端口占用
    SiteMesh使用(2.4.2)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3879.html
Copyright © 2011-2022 走看看