zoukankan      html  css  js  c++  java
  • [SDOI2016]生成魔咒

    XIV.[SDOI2016]生成魔咒

    动态SA?这怎么办?

    我们考虑往每个后缀后面全都加入一个数。很明显,如果这样搞的话,你必须每加入一个数后都要重新后缀排序,不太可能完成。

    这时,我们发现,如果这不是加入一个数,而是加入一整条后缀,那就会轻松很多,一个平衡树就能搞定。

    思考后会发现,如果我们将整个串翻转,则原本是加入一个数,现在则变成了加入一条后缀!并且,翻转对答案并无影响——毕竟本质不同子串数无论正着来还是反着来都是一样的。

    当你加入一条后缀时,所有新增加的子串数量,就等于\(n-i-\max\limits_{j>i}\{\operatorname{LCP}(i,j)\}\)

    应用\(\text{LCP Lemma}\),我们知道\(\operatorname{LCP}(i,j)=\min\limits_{rk_i<k\leq rk_j}ht_k\)

    则显然,只有\(rk_i\)左右侧的\(rk_j\)才会是最大值。于是我们就可以用一个std::set来维护所有的\(rk_j\),记为\(\mathbb{S}\)。则每加入一个数,就在\(\mathbb{S}\)中二分到前后的\(rk_j\)然后求\(\operatorname{LCP}\)即可。

    复杂度\(O(n\log n)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    namespace Suffix_Array{
    	const int N=100100;
    	int x[N],y[N],sa[N],ht[N],rk[N],buc[N],n,m,s[N];
    	bool mat(int a,int b,int k){
    		if(y[a]!=y[b])return false;
    		if((a+k<n)^(b+k<n))return false;
    		if((a+k<n)&&(b+k<n))return y[a+k]==y[b+k];
    		return true;
    	}
    	void SA(){
    		for(int i=0;i<n;i++)buc[x[i]=s[i]]++;
    		for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    		for(int i=n-1;i>=0;i--)sa[--buc[x[i]]]=i;
    		for(int k=1;k<n;k<<=1){
    			int num=0;
    			for(int i=n-k;i<n;i++)y[num++]=i;
    			for(int i=0;i<n;i++)if(sa[i]>=k)y[num++]=sa[i]-k;
    			for(int i=0;i<=m;i++)buc[i]=0;
    			for(int i=0;i<n;i++)buc[x[y[i]]]++;
    			for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    			for(int i=n-1;i>=0;i--)sa[--buc[x[y[i]]]]=y[i],y[i]=0;
    			swap(x,y);
    			x[sa[0]]=num=0;
    			for(int i=1;i<n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
    			m=num;
    		}
    		for(int i=0;i<n;i++)rk[sa[i]]=i;
    		for(int i=0,k=0;i<n;i++){
    			if(!rk[i])continue;
    			if(k)k--;
    			int j=sa[rk[i]-1];
    			while(j+k<n&&i+k<n&&s[j+k]==s[i+k])k++;
    			ht[rk[i]]=k;
    		}
    	}	
    }
    using namespace Suffix_Array;
    int mn[100100][20],LG[100100];
    int LCP(int l,int r){//get the LCP of suffix l and r;
    	l++;
    	int k=LG[r-l+1];
    	return min(mn[l][k],mn[r-(1<<k)+1][k]);
    }
    vector<int>v;
    set<int>st;
    ll res;
    int main(){
    	scanf("%d",&n);
    	for(int i=0;i<n;i++)scanf("%d",&s[i]),v.push_back(s[i]);
    	sort(v.begin(),v.end()),v.resize(m=unique(v.begin(),v.end())-v.begin());
    	for(int i=0;i<n;i++)s[i]=lower_bound(v.begin(),v.end(),s[i])-v.begin()+1;
    	reverse(s,s+n);
    	SA();
    	for(int i=2;i<n;i++)LG[i]=LG[i>>1]+1;
    	for(int i=1;i<n;i++)mn[i][0]=ht[i];
    	for(int j=1;j<=LG[n-1];j++)for(int i=1;i+(1<<j)-1<n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
    	for(int i=n-1;i>=0;i--){
    		int sm=0;
    		auto it=st.upper_bound(rk[i]);
    		if(it!=st.end())sm=max(sm,LCP(rk[i],*it));
    		if(it!=st.begin())it--,sm=max(sm,LCP(*it,rk[i]));
    		st.insert(rk[i]);
    		res+=n-i-sm;
    		printf("%lld\n",res);
    	}
    	return 0;
    } 
    

  • 相关阅读:
    如何使用SAP Intelligent Robotic Process Automation自动操作Excel
    OpenSAML 使用引导 IV: 安全特性
    Spring Cloud Zuul 网关使用与 OAuth2.0 认证授权服务
    微服务架构集大成者—Spring Cloud (转载)
    Spring Cloud Eureka 服务注册列表显示 IP 配置问题
    使用 Notification API 开启浏览器桌面提醒
    SignalR 中使用 MessagePack 序列化提高 WebSocket 通信性能
    配置 Nginx 的目录浏览功能
    关于 Nginx 配置 WebSocket 400 问题
    Migrate from ASP.NET Core 2.0 to 2.1
  • 原文地址:https://www.cnblogs.com/Troverld/p/14605222.html
Copyright © 2011-2022 走看看