zoukankan      html  css  js  c++  java
  • [NOI2015]品酒大会

    [NOI2015]品酒大会

    题目大意:

    一个长度为(n(nle3 imes10^5))的字符串(s(s_iin[ ext{'a'},'z'])),若对于后缀(i,j)(operatorname{lcp}(i,j)le r),则我们称后缀对((i,j))为「(r)相似」的。另有数列(a_{1sim n}),表示后缀对((i,j))的贡献为(a_icdot a_j)。对于(r=0,1,ldots,n-1),求「(r)相似」的后缀对有多少组,其中贡献的最大值为多少。

    思路:

    首先用后缀数组求出字符串(s)(lcp)数组。对于(lcp)数组排序,按从大到小顺序合并(lcp=i)对应的两个后缀所属的联通块,合并后块内任意两个后缀的(operatorname{lcp})一定(ge r),即一定为「(r)相似」的。对于每个连通块,维护(a_i)的最大值、次大值、最小值、次小值即可求得两数之积的最大值。对于方案数,可以简单地求出合并后各连通块内两两组合的方案数之和。

    使用(O(nlog^2n))的后缀数组可以在洛谷、LOJ、UOJ上通过,BZOJ会被卡,需要使用更优秀的算法。

    源代码:

    #include<cstdio>
    #include<cctype>
    #include<vector>
    #include<climits>
    #include<algorithm>
    #include<functional>
    typedef long long int64;
    inline int getint() {
    	register char ch;
    	register bool neg=false;
    	while(!isdigit(ch=getchar())) neg|=ch=='-';
    	register int x=ch^'0';
    	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    	return neg?-x:x;
    }
    const int N=3e5+1;
    char s[N];
    int n,k,sa[N],rank[N],tmp[N];
    inline bool cmp(const int &i,const int &j) {
    	if(rank[i]!=rank[j]) return rank[i]<rank[j];
    	const int ri=i+k<=n?rank[i+k]:-1;
    	const int rj=j+k<=n?rank[j+k]:-1;
    	return ri<rj;
    }
    inline void suffix_sort() {
    	for(register int i=0;i<=n;i++) {
    		sa[i]=i;
    		rank[i]=s[i];
    	}
    	for(k=1;k<=n;k<<=1) {
    		std::sort(&sa[0],&sa[n]+1,cmp);
    		tmp[sa[0]]=0;
    		for(register int i=1;i<=n;i++) {
    			tmp[sa[i]]=tmp[sa[i-1]]+cmp(sa[i-1],sa[i]);
    		}
    		std::copy(&tmp[0],&tmp[n]+1,rank);
    	}
    }
    struct Node {
    	int val,id;
    	bool operator > (const Node &rhs) const {
    		return val>rhs.val;
    	}
    };
    Node lcp[N];
    inline void init_lcp() {
    	for(register int i=0,h=0;i<n;i++) {
    		if(h>0) h--;
    		const int &j=sa[rank[i]-1];
    		while(i+h<n&&j+h<n&&s[i+h]==s[j+h]) h++;
    		lcp[i]=(Node){h,i};
    	}
    }
    int m[2][2][N];
    int64 ans[N],cnt[N];
    struct DisjointSet {
    	int anc[N],size[N];
    	void reset(const int &n) {
    		for(register int i=1;i<=n;i++) {
    			anc[i]=i;
    			size[i]=1;
    		}
    	}
    	int find(const int &x) {
    		return x==anc[x]?x:anc[x]=find(anc[x]);
    	}
    };
    DisjointSet djs;
    int main() {
    	n=getint();
    	scanf("%s",s);
    	for(register int i=1;i<=n;i++) {
    		m[0][0][i]=m[1][0][i]=getint();
    		m[0][1][i]=INT_MAX;
    		m[1][1][i]=INT_MIN;
    	}
    	suffix_sort();
    	init_lcp();
    	std::sort(&lcp[0],&lcp[n],std::greater<Node>());
    	djs.reset(n);
    	ans[n]=LLONG_MIN;
    	for(register int i=n-1,j=0;~i;i--) {
    		cnt[i]=cnt[i+1];
    		ans[i]=ans[i+1];
    		for(;j<n&&lcp[j].val>=i;j++) {
    			const int u=djs.find(lcp[j].id+1);
    			const int v=djs.find(sa[rank[lcp[j].id]-1]+1);
    			if(u==v||u==0||v==0) continue;
    			djs.anc[u]=v;
    			cnt[i]-=(int64)djs.size[u]*(djs.size[u]-1)/2+(int64)djs.size[v]*(djs.size[v]-1)/2;
    			djs.size[v]+=djs.size[u];
    			cnt[i]+=(int64)djs.size[v]*(djs.size[v]-1)/2;
    			for(register int i=0;i<2;i++) {
    				for(register int j=i;j<2;j++) {
    					if(m[0][i][u]<m[0][j][v]) std::swap(m[0][i][u],m[0][j][v]);
    					if(m[1][i][u]>m[1][j][v]) std::swap(m[1][i][u],m[1][j][v]);
    				}
    			}
    			ans[i]=std::max(ans[i],std::max((int64)m[0][0][v]*m[0][1][v],(int64)m[1][0][v]*m[1][1][v]));
    		}
    	}
    	for(register int i=0;i<n;i++) {
    		printf("%lld %lld
    ",cnt[i],ans[i]==LLONG_MIN?0:ans[i]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    RMI方式Ehcache集群的源码分析
    Bootstrap简单HelloWorld入门教程
    深入剖析Tomcat会话机制
    Redis主从和HA配置
    Ajax
    java数组复制===clone()
    java使用array.copy复制数组
    java数组复制的简单方法(一)
    java ------------用Array.sort()输出数组元素的最大值,最小值,平均值~~~~
    java从键盘输入若干数,求其最大值,最小值,平均值。等等
  • 原文地址:https://www.cnblogs.com/skylee03/p/9211277.html
Copyright © 2011-2022 走看看