loj 6198

题目链接

题意

给了一个字符串,字符串每个位置有一个权值 w i wi .定义字符串的两个后缀 i , j i,j 的价值为 L C P ( i , j ) + LCP(i,j)+ w i wi xor w j wj

求最大价值

解法

首先分开考虑:对于 L C P ( i , j ) LCP(i,j) ,可以先求出sa,和height,然后是[i,j]的区间min.
对于 w i wi xor w j wj .,可以用01trie求.
然后把两个价值统一起来的方法就是把height从大到小排序,保证每个加进去的都是最小的一个,然后还需要用并查集维护现在一个区间有哪些位置,然后就用trie在这些数里选出最大值就可以了.并查集合并的时候需要启发式合并.

注意:我现在对sa的记忆和理解有写模糊了.需要在这里备忘一下:
sa[]:排名为i的后缀是哪个
rk[]:第i个后缀的排名
h[]:排名为i的后缀和排名为i-1的后缀的lcp

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
const int mx=20;
inline int read(){
	char c=getchar();int t=0,f=1;
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,w[maxn],x[maxn],y[maxn],sa[maxn],rk[maxn],h[maxn],t[maxn],m=300,c[maxn*mx][2];
char s[maxn];
void get(){
	for(int i=1;i<=n;i++)t[x[i]=s[i]]++;
	for(int i=1;i<=m;i++)t[i]+=t[i-1];
	for(int i=n;i>=1;i--)sa[t[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1){
		int num=0;
		for(int i=n-k+1;i<=n;i++)y[++num]=i;
		for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
		memset(t,0,sizeof(t));
		for(int i=1;i<=n;i++)t[x[i]]++;
		for(int i=1;i<=m;i++)t[i]+=t[i-1];
		for(int i=n;i>=1;i--){sa[t[x[y[i]]]--]=y[i];y[i]=0;}
		swap(x,y);
		x[sa[1]]=1;num=1;
		for(int i=2;i<=n;i++)
		x[sa[i]]=(y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
		if(num==n)break;
		m=num;
	}
	for(int i=1;i<=n;i++)rk[sa[i]]=i;
	int k=0;
	for(int i=1;i<=n;i++){//求height,枚举i而不是rk[i]
		if(k)k--;int j=sa[rk[i]-1];
		while(s[i+k]==s[j+k]&&i+k<=n&&j+k<=n)k++;
		h[rk[i]]=k;
	}
}
int sz[maxn],f[maxn],rt[maxn],cnt;
inline int find(int x){
	return f[x]==x?f[x]:f[x]=find(f[x]);
}
int build(int x){
	int rt=++cnt,now=rt;
	for(int i=mx;i>=0;i--){
		c[now][(x>>i)&1]=++cnt,now=cnt;
	}
	return rt;
}
typedef pair<int,int> pii;
pii so[maxn];
int query(int x,int y,int z=0){
	int best=z;
	if(c[x][0]){
		if(c[y][1])best=query(c[x][0],c[y][1],2*z+1);
		else best=query(c[x][0],c[y][0],2*z);
	}
	if(c[x][1]){
		if(c[y][0])best=max(best,query(c[x][1],c[y][0],2*z+1));
		else best=max(best,query(c[x][1],c[y][1],2*z));
	}
	return best;
}
int ans;
void merge(int x,int y){//trie树合并类似线段树合并,这里不需要新建节点是因为没有修改.
	if(c[x][0]){
		if(c[y][0])merge(c[x][0],c[y][0]);
		else c[y][0]=c[x][0];
	}
	if(c[x][1]){
		if(c[y][1])merge(c[x][1],c[y][1]);
		else c[y][1]=c[x][1];
	}
}
int main(){
	n=read();
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)w[i]=read();
	get();
	for(int i=1;i<=n;i++){
		sz[i]=1,f[i]=i;rt[i]=build(w[sa[i]]);
	}
	for(int i=2;i<=n;i++){
		so[i-1]=pii(n-h[i],i);
	}
	sort(so+1,so+n);
	int ret=0;
	for(int i=1;i<n;i++){
		int x=so[i].second,y=x-1,z=n-so[i].first;
		x=find(x),y=find(y);
		if(sz[x]<sz[y])swap(x,y);
		int tmp=query(rt[y],rt[x])+z;
		if(tmp>ans)ans=tmp;
		sz[x]+=sz[y];
		merge(rt[y],rt[x]);
		f[y]=x;
	}
	printf("%d\n",ans);
	return 0;
}

发布了62 篇原创文章 · 获赞 1 · 访问量 1006

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/103733176