zoukankan      html  css  js  c++  java
  • 「SOL」谢特(LOJ)

    Oh! s**t bi——(屏蔽)


    # 题面

    给定一个长度为 (n) 的字符串 (S) 和正整数数列 (w_1,w_2,cdots,w_n)。记以第 (i) 个字符开头的后缀为 (S_i)

    (maxlimits_{i eq j}{operatorname{LCP}(S_i,S_j)+(w_ioplus w_j)}),其中 (oplus) 为按位异或。

    数据规模:(1le nle10^5)(w_i< n)


    # 解析

    LCP?能够快速求LCP的——SAM,哈希+二分……SA的height数组?(虽然SAM也能做,但是感觉height数组要清晰一些)

    另外,我写的 height[i] 表示的是 sa[i]sa[i+1] 的 LCP,注意一下下标。

    我们知道 (LCP(S_i,S_j)) 就是 rank[i]rank[j] 之间的 height 的最小值。

    不妨对 height 数组的每个位置 (i),考虑计算以 (i) 处为 height 最小值的一对 (S_i,S_j)(max{w_ioplus w_j})。如何求两个数异或的最大值?Trie树即可。(线性基是求若干个数的异或和)

    那么怎么求有哪些 (S_i,S_j)(i) 位置为 height 最小值?可以想到用笛卡尔树,按根为区间最小值的方式建出 height 数组的笛卡尔树。

    于是可以递归地计算答案——

    • 叶子 (i) 处答案为 height[i]+(w[i]^w[i+1]),笛卡尔树中有两个串:w[sa[i]],w[sa[i+1]]
    • 非叶子节点 (i),先考虑计算答案——枚举一棵子树的中的所有串,在另一棵子树的 Trie 树中查答案,取最大值记为 mx,则答案为 height[i]+mx
      但是如果随便选一棵子树枚举其中的所有串,每次复杂度显然会降至 (O(nlog n))。我们可以“聪明一点”,用启发式的思想,枚举大小较小的一棵子树中的串,在另一棵子树的 Trie 中查,这样总的复杂度就是 (O(nlog^2n))
      最后两棵子树的 Trie 合并,该怎么合并怎么合并,至于复杂度:未合并时,Trie 的总点数为 (O(nlog n)),每次合并的复杂度是合并的点数,每次合并后总点数会减少合并的点数,总复杂度 (O(nlog n))

    这样就可以在 (O(nlog^2 n)) 的复杂度内解决。

    最后提一句——注意 Trie 树存二进制的叶子节点的深度(在我的实现中叶子深度应为 (-1))。


    # 源代码

    解析说起来还很快乐,但是代码好像也不是特别短……

    点击展开/折叠代码
    /*Lucky_Glass*/
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N=1e5+10;
    #define con(type) const type &
    
    inline int rin(int &r){
    	int b=1,c=getchar();r=0;
    	while(c<'0' || '9'<c) b=c=='-'?-1:b,c=getchar();
    	while('0'<=c && c<='9') r=(r<<1)+(r<<3)+(c^'0'),c=getchar();
    	return r*=b;
    }
    
    namespace SA{
    	int rnk[N<<1],nrnk[N<<1],sa[N<<1],nsa[N<<1],hgt[N];
    	int bin[N];
    	void build(char *str,con(int)n){
    		for(int i=1;i<=n;i++) bin[str[i]-'a']++;
    		for(int i=1;i<30;i++) bin[i]+=bin[i-1];
    		for(int i=1;i<=n;i++) sa[bin[str[i]-'a']--]=i;
    		rnk[sa[1]]=1;
    		for(int i=2;i<=n;i++){
    			rnk[sa[i]]=rnk[sa[i-1]];
    			if(str[sa[i]]!=str[sa[i-1]]) rnk[sa[i]]++;
    		}
    		for(int len=1;rnk[sa[n]]<n;len<<=1){
    			for(int i=1;i<=n;i++) bin[rnk[sa[i]]]=i;
    			for(int i=n;i>=1;i--)
    				if(sa[i]>len)
    					nsa[bin[rnk[sa[i]-len]]--]=sa[i]-len;
    			for(int i=n-len+1;i<=n;i++)
    				nsa[bin[rnk[i]]--]=i;
    			nrnk[nsa[1]]=1;
    			for(int i=2;i<=n;i++){
    				nrnk[nsa[i]]=nrnk[nsa[i-1]];
    				if(rnk[nsa[i]]!=rnk[nsa[i-1]] || rnk[nsa[i]+len]!=rnk[nsa[i-1]+len])
    					nrnk[nsa[i]]++;
    			}
    			swap(rnk,nrnk),swap(sa,nsa);
    		}
    	}
    	void buildHeight(char *str,con(int)n){
    		for(int i=1,tmp=0;i<=n;i++){
    			if(tmp) tmp--;
    			while(str[i+tmp]==str[sa[rnk[i]-1]+tmp]) tmp++;
    			hgt[rnk[i]]=tmp;                                                 
    		}
    	}
    }
    
    struct SuperDeque{
    	int ary[N],le,ri;
    	void clear(){le=1,ri=0;}
    	void push_back(con(int)x){ary[++ri]=x;}
    	void pop_back(){ri--;}
    	void pop_front(){le++;}
    	int back(){return ary[ri];}
    	int front(){return ary[le];}
    	bool empty(){return le>ri;}
    }que;
    
    struct Trie{
    	int nxt[N*20][2],rt[N],ncnt;
    	int combine(con(int)u,con(int)v){
    		if(!u || !v) return u|v;
    		nxt[u][0]=combine(nxt[u][0],nxt[v][0]);
    		nxt[u][1]=combine(nxt[u][1],nxt[v][1]);
    		return u;
    	}
    	int query(con(int)u,con(int)num){
    		int ret=0;
    		for(int dep=16,v=u;~dep;dep--){
    			int dir=!(num>>dep&1);
    			if(nxt[v][dir]) v=nxt[v][dir],ret|=(1<<dep);
    			else v=nxt[v][!dir];
    		}
    		return ret;
    	}
    	int query(con(int)u,con(int)dep,con(int)num,con(int)v){
    		if(dep==-1) return query(v,num);
    		int ret=0;
    		if(nxt[u][0]) ret=max(ret,query(nxt[u][0],dep-1,num,v));
    		if(nxt[u][1]) ret=max(ret,query(nxt[u][1],dep-1,num|(1<<dep),v));
    		return ret;
    	}
    	void insert(int &u,con(int)dep,con(int)num){
    		if(!u) u=++ncnt;
    		if(dep==-1) return;
    		insert(nxt[u][num>>dep&1],dep-1,num);
    	}
    	int& operator [](con(int)u){return rt[u];}
    }tri;
    
    int n,ans;
    int val[N],son[N][2];
    char str[N];
    
    int dacDFS(con(int)u){
    	if(!son[u][0] && !son[u][1]){
    		tri.insert(tri[u],16,val[SA::sa[u]]);
    		ans=max(ans,tri.query(tri[u],val[SA::sa[u+1]])+SA::hgt[u]);
    		tri.insert(tri[u],16,val[SA::sa[u+1]]);
    		return 2;
    	}
    	else if(!son[u][0]){
    		int ret=dacDFS(son[u][1])+1;
    		ans=max(ans,tri.query(tri[son[u][1]],val[SA::sa[u]])+SA::hgt[u]);
    		tri.insert(tri[son[u][1]],16,val[SA::sa[u]]);
    		tri[u]=tri[son[u][1]];
    		return ret;
    	}
    	else if(!son[u][1]){
    		int ret=dacDFS(son[u][0])+1;
    		ans=max(ans,tri.query(tri[son[u][0]],val[SA::sa[u+1]])+SA::hgt[u]);
    		tri.insert(tri[son[u][0]],16,val[SA::sa[u+1]]);
    		tri[u]=tri[son[u][0]];
    		return ret;
    	}
    	else{
    		int res0=dacDFS(son[u][0]),res1=dacDFS(son[u][1]);
    		if(res0<res1) ans=max(ans,tri.query(tri[son[u][0]],16,0,tri[son[u][1]]));
    		else ans=max(ans,tri.query(tri[son[u][1]],16,0,tri[son[u][0]]));
    		tri[u]=tri.combine(tri[son[u][0]],tri[son[u][1]]);
    		return res0+res1;
    	}
    }
    int main(){
    	rin(n),scanf("%s",str+1);
    	for(int i=1;i<=n;i++) rin(val[i]);
    	SA::build(str,n),SA::buildHeight(str,n);
    	for(int i=1;i<n;i++) SA::hgt[i]=SA::hgt[i+1];
    	// for(int i=1;i<n;i++) printf("! %d
    ",SA::hgt[i]);
    	que.clear();
    	for(int i=1;i<n;i++){
    		int las=0;
    		while(!que.empty() && SA::hgt[que.back()]>SA::hgt[i]){
    			son[que.back()][1]=las;
    			las=que.back(),que.pop_back();
    		}
    		son[i][0]=las;
    		que.push_back(i);
    	}
    	int las=0;
    	while(!que.empty()){
    		son[que.back()][1]=las;
    		las=que.back(),que.pop_back();
    	}
    	// for(int i=1;i<n;i++) printf("%d %d
    ",son[i][0],son[i][1]);
    	dacDFS(las);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    THE END

    Thanks for reading!

    若你是最悠长的宫音
    我是清亮的羽
    分离在琴弦的两极 难相遇
    纵然是再遥远的距离
    也愿与你相依
    只等那一曲琴声起 和鸣

    ——《琴弦上(vocaloid)》By 乐正绫/赤羽/星葵

    > Link 琴弦上-Bilibili

    欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~
  • 相关阅读:
    今天开始学习WCF了开发环境的配置
    我的文章《DLL封装登录框架实现代码复用》终于要发表了
    python小练习给右键菜单添加用chrome打开
    手动创建oracle数据库
    Sublime Text 2中如何输入中文
    转 用Python画Mandelbrot集
    Win7 64位安装Python PIL库
    推荐一本有关设计的好书《写给大家看的设计书》
    坚持做自己
    更新专业版显卡驱动导致ubuntu无法进入桌面环境
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/14457432.html
Copyright © 2011-2022 走看看