• [bzoj4199][Noi2015]品酒大会——后缀数组


    题目大意:

    给定一个序列,定义两个后缀是k相似的当且仅当这两个后缀有长度为k的公共前缀。
    求对任意(rin [0,n-1])(r)相似的后缀的对数和两个后缀乘积的最大值。

    思路:

    先考虑后缀数组是如何计算两个后缀的lcp,发现是对于一段连续的height取min。
    于是对于制定的相似度r,height < r的位置必定是两个后缀不能越过的,于是不难发现将有height (ge)r的位置给取出来,然后整个序列分成了若干个连通块,一对具有r相似的后缀必定同时在一个联通快里面。

    对于一个连通块,我们要得到答案所需要的信息即为最大值,次大值,最小值,次小值和长度。

    于是直接从大到小计算答案,每次将若干个块连接,中间的过程用并查集维护。

    /*=======================================
     * Author : ylsoi
     * Time : 2019.2.6
     * Problem : luogu2178
     * E-mail : ylsoi@foxmail.com
     * ====================================*/
    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
    #define debug(x) cout<<#x<<"="<<x<<" "
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    typedef long long ll;
    
    using namespace std;
    
    void File(){
        freopen("luogu2178.in","r",stdin);
        freopen("luogu2178.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	_=0; T fl=1; char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
    	for(;isdigit(ch);ch=getchar())_=(_<<1)+(_<<3)+(ch^'0');
    	_*=fl;
    }
    
    const int maxn=3e5+10;
    const int inf=0x3f3f3f3f;
    const ll INF=2e18;
    int n;
    char s[maxn];
    ll a[maxn];
    int sz,sa[maxn],rk[maxn],tp[maxn],tax[maxn],height[maxn];
    
    void radix_sort(){
    	REP(i,1,sz)tax[i]=0;
    	REP(i,1,n)++tax[rk[i]];
    	REP(i,1,sz)tax[i]+=tax[i-1];
    	DREP(i,n,1)sa[tax[rk[tp[i]]]--]=tp[i];
    }
    
    void suffix_sort(){
    	sz=26;
    	REP(i,1,n)rk[i]=s[i]-'a'+1,tp[i]=i;
    	radix_sort();
    	for(int w=1,p=0;w<n;w<<=1){
    		p=0;
    		REP(i,1,w)tp[++p]=n-w+i;
    		REP(i,1,n)if(sa[i]>w)tp[++p]=sa[i]-w;
    		radix_sort();
    		swap(rk,tp);
    		rk[sa[1]]=p=1;
    		REP(i,2,n)
    			if(tp[sa[i-1]]==tp[sa[i]] && tp[sa[i-1]+w]==tp[sa[i]+w])rk[sa[i]]=p;
    			else rk[sa[i]]=++p;
    		sz=p;
    		if(sz==n)break;
    	}
    	//REP(i,1,n)cout<<s+sa[i]<<endl;
    	int p=0;
    	REP(i,1,n){
    		if(p)--p;
    		int j=sa[rk[i]-1];
    		while(s[i+p]==s[j+p])++p;
    		height[rk[i]]=p;
    	}
    }
    
    struct node{
    	ll l,r,mx[2],mn[2];
    }c[maxn];
    
    node operator + (node x,node y){
    	node ret;
    	ret.l=min(x.l,y.l);
    	ret.r=max(x.r,y.r);
    	if(x.mx[0]<y.mx[0])swap(x,y);
    	ret.mx[0]=x.mx[0];
    	ret.mx[1]=max(x.mx[1],y.mx[0]);
    	if(x.mn[0]>y.mn[0])swap(x,y);
    	ret.mn[0]=x.mn[0];
    	ret.mn[1]=min(x.mn[1],y.mn[0]);
    	return ret;
    }
    
    int fa[maxn];
    
    int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}
    
    ll sum,mx=-INF;
    node t;
    
    void merge(int x,int y){
    	x=find(x),y=find(y);
    	//debug(x),debug(y)<<endl;
    	if(x==y)return;
    	sum-=(c[x].r-c[x].l+1)*(c[x].r-c[x].l+2)/2;
    	sum-=(c[y].r-c[y].l+1)*(c[y].r-c[y].l+2)/2;
    	c[x]=c[x]+c[y];
    	sum+=(c[x].r-c[x].l+1)*(c[x].r-c[x].l+2)/2;
    	t=c[c[x].l-1]+c[x];
    	mx=max(mx,t.mx[0]*t.mx[1]);
    	mx=max(mx,t.mn[0]*t.mn[1]);
    	fa[y]=x;
    }
    
    vector<int>lis[maxn];
    pair<ll,ll>ans[maxn];
    
    void work(){
    	REP(i,1,n){
    		fa[i]=i;
    		c[i]=(node){i,i,{a[sa[i]],-inf},{a[sa[i]],inf}};
    		if(i>1)lis[height[i]].pb(i);
    	}
    	DREP(i,n-1,0){
    		REP(j,0,lis[i].size()-1){
    			++sum;
    			mx=max(mx,c[lis[i][j]].mx[0]*c[lis[i][j]-1].mx[0]);
    		}
    		//debug(i)<<endl;
    		REP(j,0,lis[i].size()-1){
    			//debug(lis[i][j]);
    			if(lis[i][j]>2 && height[lis[i][j]-1]>=i)merge(lis[i][j],lis[i][j]-1);
    			if(lis[i][j]<n && height[lis[i][j]+1]>=i)merge(lis[i][j],lis[i][j]+1);
    		}
    		//cout<<endl;
    		ans[i]=mk(sum,mx);
    	}
    	REP(i,0,n-1)printf("%lld %lld
    ",ans[i].fi,ans[i].se==-INF ? 0 : ans[i].se);
    }
    
    int main(){
        File();
    	read(n);
    	scanf("%s",s+1);
    	REP(i,1,n)read(a[i]);
    	suffix_sort();
    	work();
        return 0;
    }
    
    
  • 相关阅读:
    左旋转字符串
    mybitis实现增,删,改,查,模糊查询的两种方式:(1)
    使用MYSQL时遇到的问题解决方法
    mysql的使用命令(1)
    matlab_exercise(4)----第一题
    matlab_exercise(3)----海伦公式求三角形面积
    matlab_exercise(2)----输入一个三位数,依次输出其个位数字,十位数字,百位数字
    matlab_exercise(1)--交换值
    [xPlugins] jQuery Contextmenu右键菜单
    [网站安全] 十大抢手的网站压力测试工具
  • 原文地址:https://www.cnblogs.com/ylsoi/p/10353980.html
Copyright © 2020-2023  润新知