zoukankan      html  css  js  c++  java
  • [LOJ6198]谢特

    loj

    description

    给你一个字符串和一个数组(w_i),定义(mbox{LCP}(i,j))(i,j)两个后缀的最长公共前缀。求(max_{i,j}mbox{LCP}(i,j)+(w_i mbox{xor} w_j))
    (n le 10^5)

    sol

    首先,(mbox{LCP}(i,j))是后缀排序上的一段连续区间的(mbox{Height})最小值。
    那么我们可以枚举这个最小值出现的位置,那么跨越这个位置的所有点对的(mbox{LCP})就确定了。
    接下来我们只要考虑最大化(w_i mbox{xor} w_j)
    显然可以用可持久化(mbox{Tire})树实现,复杂度是查询次数( imes log n)的。

    算法流程大致是这样的:先找到(mbox{Height})最小的位置(p),计算所有跨越(p)的点对的答案。枚举分割点两侧(size)较小的一侧,在可持久化(mbox{Tire})树上查询它和另一侧异或的最大值。接着两侧被完全割裂开,可以分别递归下去处理。

    考虑这个东西的时间复杂度。可以发现这就是一个倒着做的启发式合并,所以复杂度就是(O(nlog^2n))
    当然也可以按(mbox{Height})从大到小启发式合并,复杂度是一样的。

    code

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 1e5+5;
    struct node{int ch[2],sz;}tr[N*35];
    int n,t[N],x[N],y[N],SA[N],Rank[N],Height[N],st[19][N],lg[N],rt[N],tot,w[N],ans;
    char s[N];
    bool cmp(int i,int j,int k){
    	return y[i]==y[j]&&y[i+k]==y[j+k];
    }
    void getSA(){
    	int m=30;
    	for (int i=1;i<=n;++i) ++t[x[i]=s[i]-'a'+1];
    	for (int i=1;i<=m;++i) t[i]+=t[i-1];
    	for (int i=n;i;--i) SA[t[x[i]]--]=i;
    	for (int k=1;k<=n;k<<=1){
    		int p=0;
    		for (int i=0;i<=m;++i) y[i]=0;
    		for (int i=n-k+1;i<=n;++i) y[++p]=i;
    		for (int i=1;i<=n;++i) if (SA[i]>k) y[++p]=SA[i]-k;
    		for (int i=0;i<=m;++i) t[i]=0;
    		for (int i=1;i<=n;++i) ++t[x[y[i]]];
    		for (int i=1;i<=m;++i) t[i]+=t[i-1];
    		for (int i=n;i;--i) SA[t[x[y[i]]]--]=y[i];
    		swap(x,y);x[SA[1]]=p=1;
    		for (int i=2;i<=n;++i) x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
    		if (p>=n) break;m=p;
    	}
    	for (int i=1;i<=n;++i) Rank[SA[i]]=i;
    	for (int i=1,j=0;i<=n;++i){
    		if (j) --j;
    		while (s[i+j]==s[SA[Rank[i]-1]+j]) ++j;
    		Height[Rank[i]]=j;st[0][i]=i;
    	}
    	for (int i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
    	for (int j=1;j<=lg[n];++j)
    		for (int i=1;i+(1<<j)-1<=n;++i)
    			if (Height[st[j-1][i]]<Height[st[j-1][i+(1<<j-1)]])
    				st[j][i]=st[j-1][i];
    			else st[j][i]=st[j-1][i+(1<<j-1)];
    }
    int cal(int l,int r){
    	int k=lg[r-l+1];
    	if (Height[st[k][l]]<Height[st[k][r-(1<<k)+1]]) return st[k][l];
    	return st[k][r-(1<<k)+1];
    }
    void modify(int &x,int p,int dep){
    	tr[++tot]=tr[x];++tr[x=tot].sz;
    	if (!~dep) return;
    	modify(tr[x].ch[(p>>dep)&1],p,dep-1);
    }
    int query(int x,int y,int p,int dep){
    	if (!~dep) return 0;
    	int c=(p>>dep)&1;c^=1;
    	if (tr[tr[x].ch[c]].sz-tr[tr[y].ch[c]].sz) return query(tr[x].ch[c],tr[y].ch[c],p,dep-1)|(1<<dep);
    	c^=1;return query(tr[x].ch[c],tr[y].ch[c],p,dep-1);
    }
    void solve(int l,int r){
    	if (l==r) return;int mid=cal(l+1,r)-1;
    	if (mid-l+1<=r-mid){
    		for (int i=l;i<=mid;++i)
    			ans=max(ans,Height[mid+1]+query(rt[r],rt[mid],w[SA[i]],18));
    	}else{
    		for (int i=mid+1;i<=r;++i)
    			ans=max(ans,Height[mid+1]+query(rt[mid],rt[l-1],w[SA[i]],18));
    	}
    	solve(l,mid);solve(mid+1,r);
    }
    int main(){
    	n=gi();scanf("%s",s+1);getSA();
    	for (int i=1;i<=n;++i) w[i]=gi();
    	for (int i=1;i<=n;++i) modify(rt[i]=rt[i-1],w[SA[i]],18);
    	solve(1,n);printf("%d
    ",ans);return 0;
    }
    
  • 相关阅读:
    Struts2的原理,配置和使用
    tomcat启动异常之----A child container failed during start
    Oracle两表关联更新其中一张表的数据
    jsp隐藏字符串中间部分信息,只显示前后字段
    浏览器会缓存js文件
    tomcat启动项目很快,且不报错,访问报404,项目实际上没起来,起的是空tomcat
    jd-eclipse插件的安装
    Junit4使用总结
    json中dump()与dumps()里的参数解释
    python时间函数和常用格式化
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/9385717.html
Copyright © 2011-2022 走看看