• LOJ#6198. 谢特


    XXXVI.LOJ#6198. 谢特

    SA+笛卡尔树+01trie+启发式合并模板四合一,省选模板练习必备神器

    考虑SA后建立笛卡尔树。问题转换为在笛卡尔树的一段区间中(此时该区间内任意两条后缀的LCP长度均为区间中 \(ht\) 最小值)任意两条后缀的 \(\text{xor}\) 最大值。是经典的01trie问题,考虑建立01trie。则,父节点的01trie可以由两个子节点的01trie启发式合并(或者说,因为笛卡尔树也是树,所以是dsu on tree)得到。

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

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    char s[100100];
    int sa[100100],rk[100100],ht[100100],buc[100100];
    bool mat(int a,int b,int k){
    	if(ht[a]!=ht[b])return false;
    	if((a+k<n)!=(b+k<n))return false;
    	if((a+k<n)&&(b+k<n))return ht[a+k]==ht[b+k];
    	return true;
    }
    void SA(){
    	for(int i=0;i<n;i++)buc[rk[i]=s[i]-'a']++;
    	for(int i=1;i<m;i++)buc[i]+=buc[i-1];
    	for(int i=n-1;i>=0;i--)sa[--buc[rk[i]]]=i;
    	for(int k=1;k<n;k<<=1){
    		int num=0;
    		for(int i=n-k;i<n;i++)ht[num++]=i;
    		for(int i=0;i<n;i++)if(sa[i]>=k)ht[num++]=sa[i]-k;
    		for(int i=0;i<=m;i++)buc[i]=0;
    		for(int i=0;i<n;i++)buc[rk[ht[i]]]++;
    		for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    		for(int i=n-1;i>=0;i--)sa[--buc[rk[ht[i]]]]=ht[i],ht[i]=0;
    		swap(rk,ht),rk[sa[0]]=num=0;
    		for(int i=1;i<n;i++)rk[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
    		if(num>=n-1)break;
    		m=num;
    	}
    	for(int i=0;i<n;i++)ht[i]=0;
    	for(int i=0,k=0;i<n;i++){
    		if(!rk[i])continue;
    		if(k)k--;
    		int j=sa[rk[i]-1];
    		while(i+k<n&&j+k<n&&s[i+k]==s[j+k])k++;
    		ht[rk[i]]=k;
    	}
    }
    int L[100100],R[100100],ls[100100],rs[100100],stk[100100],tp,res;
    int a[100100],rt[100100],cnt,bin[10010000];
    const int LG=17;
    struct Trie{int ch[2];}t[10010000];
    int newnode(){return tp?bin[tp--]:++cnt;}
    void ins(int &x,int y,int pos){
    	if(!x)x=newnode();
    	if(pos==-1)return;
    	ins(t[x].ch[(y>>pos)&1],y,pos-1);
    }
    void erase(int &x){if(!x)return;bin[++tp]=x,erase(t[x].ch[0]),erase(t[x].ch[1]),x=0;}
    int ask(int x,int y,int pos){
    	if(pos==-1)return 0;
    	if(t[x].ch[!((y>>pos)&1)])return ask(t[x].ch[!((y>>pos)&1)],y,pos-1)+(1<<pos);
    	else return ask(t[x].ch[(y>>pos)&1],y,pos-1);
    }
    int dfs(int x){
    	int ret=0;
    	if(ls[x])ret=max(ret,dfs(ls[x]));
    	if(rs[x])ret=max(ret,dfs(rs[x]));
    	if(x-L[x]>R[x]-x+1){
    		if(ls[x])rt[x]=rt[ls[x]];else ins(rt[x],a[sa[L[x]]],LG-1);
    		if(rs[x])erase(rt[rs[x]]);
    		for(int i=x;i<=R[x];i++)ret=max(ret,ask(rt[x],a[sa[i]],LG-1)),ins(rt[x],a[sa[i]],LG-1);
    	}else{
    		if(rs[x])rt[x]=rt[rs[x]];else ins(rt[x],a[sa[R[x]]],LG-1);
    		if(ls[x])erase(rt[ls[x]]);
    		for(int i=L[x];i<x;i++)ret=max(ret,ask(rt[x],a[sa[i]],LG-1)),ins(rt[x],a[sa[i]],LG-1); 
    	}
    	res=max(res,ret+ht[x]);
    	return ret;
    }
    int main(){
    	scanf("%d%s",&n,s),m=26,SA();
    //	for(int i=0;i<n;i++)printf("%d ",i);puts("");
    //	for(int i=0;i<n;i++)printf("%d ",sa[i]);puts("");
    //	for(int i=0;i<n;i++)printf("%d ",rk[i]);puts("");
    //	for(int i=0;i<n;i++)printf("%d ",ht[i]);puts("");
    //	for(int i=0;i<n;i++)printf("%s\n",s+sa[i]);
    	for(int i=1;i<n;i++){
    		while(tp&&ht[stk[tp]]>ht[i])R[ls[i]=stk[tp--]]=i-1;
    		if(tp)L[i]=stk[tp];else L[i]=0;
    		rs[stk[tp]]=i,stk[++tp]=i;
    	}
    	while(tp)R[stk[tp--]]=n-1;
    	for(int i=0;i<n;i++)scanf("%d",&a[i]);
    //	for(int i=1;i<n;i++)printf("%d[%d,%d](%d,%d)\n",i,L[i],R[i],ls[i],rs[i]);
    	dfs(stk[1]),printf("%d\n",res);
    	return 0;
    }
    
  • 相关阅读:
    struts2+ajax实现异步刷新
    ajax实现异步刷新
    AJAX学习总结
    Java中的日期工具类
    jsp页面中用户注销的写法
    封神台靶场:第七章:GET THE PASS! 【技能点:进程中抓下管理员明文密码】
    封神台靶场:第六章:SYSTEM!POWER!【配套课时:webshel​​l控制目标实战演练】
    封神台靶场:第五章:进击!拿到Web最高权限!【配套课时:绕过防护上传木马实战演练】
    封神台靶场:第四章:为了更多的权限!留言板!【配套课时:cookie伪造目标权限 实战演练】
    封神台靶场:第三章:爆破管理员账户登录后台【配套课时:burp到支付和暴破 实战演练】
  • 原文地址:https://www.cnblogs.com/Troverld/p/14605518.html
Copyright © 2020-2023  润新知