zoukankan      html  css  js  c++  java
  • CF504E Misha and LCP on Tree 后缀自动机+树链剖分+倍增

    求树上两条路径的 LCP (树上每个节点代表一个字符) 

    总共写+调了6个多小时,终于过了~ 

    绝对是我写过的最复杂的数据结构了     

    我们对这棵树进行轻重链剖分,然后把所有的重链分正串,反串插入到广义后缀自动机中.   

    求 LCP 的话就是后缀树中两点 $LCA$ 的深度.     

    如果 $LCP$ 的长度小于两个重链的长度,就直接输出答案,否则还要继续爬重链.  

    这道题恶心之处在于将所有重链不重不漏存起来,无法形容有多恶心.   

    code: 

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <queue> 
    #include <algorithm>
    #include <stack> 
    #include <string>      
    using namespace std;    
    void setIO(string s) 
    { 
        string in=s+".in"; 
        string out=s+".out"; 
        freopen(in.c_str(),"r",stdin); 
        freopen(out.c_str(),"w",stdout); 
    }
    namespace sam
    {
        #define N 1200004        
        int last,tot;
        int ch[N][27],pre[N],mx[N];
        inline int extend(int c)     
        {
            if(ch[last][c])  
            {  
                int p=last;
                int q=ch[p][c];  
                if(mx[q]==mx[p]+1) last=q;
                else
                {
                    int nq=++tot;            
                    mx[nq]=mx[p]+1;
                    memcpy(ch[nq],ch[q],sizeof(ch[q]));
                    pre[nq]=pre[q],pre[q]=nq;
                    for(;p&&ch[p][c]==q;p=pre[p]) ch[p][c]=nq; 
                    last=nq;  
                } 
            }
            else
            {
                int np=++tot,p=last;
                mx[np]=mx[p]+1,last=np;
                for(;p&&!ch[p][c];p=pre[p]) ch[p][c]=np;
                if(!p) pre[np]=1;
                else
                {
                    int q=ch[p][c];
                    if(mx[q]==mx[p]+1) pre[np]=q;
                    else
                    {
                        int nq=++tot; 
                        mx[nq]=mx[p]+1;
                        memcpy(ch[nq],ch[q],sizeof(ch[q])); 
                        pre[nq]=pre[q],pre[np]=pre[q]=nq;
                        for(;p&&ch[p][c]==q;p=pre[p]) ch[p][c]=nq;   
                    }
                }
                last=np; 
            }
            return last; 
        }
        #undef N
    };
       
    // 优美的后缀自动机
       
    namespace suffix_tree
    {
        #define LOG 21    
        #define N 1200006 
        vector<int>G[N];
        int fa[LOG][N],dis[N];               
        void dfs(int u)
        {
            int i,j;      
            dis[u]=dis[sam::pre[u]]+1;                    
            fa[0][u]=sam::pre[u];  
            for(i=1;i<LOG;++i) fa[i][u]=fa[i-1][fa[i-1][u]];
            for(i=0;i<(int)G[u].size();++i) dfs(G[u][i]);        
        }
        inline void build()
        {
            int i,j;
            for(i=2;i<=sam::tot;++i)                
                G[sam::pre[i]].push_back(i);   
            dfs(1);  
        }
        inline int jump(int x,int len)
        {
            for(int i=LOG-1;i>=0;--i) if(sam::mx[fa[i][x]]>=len) x=fa[i][x]; 
            return x;
        }
        inline int LCA(int x,int y)
        {
            if(dis[x]!=dis[y])
            {
                if(dis[x]>dis[y]) swap(x,y);  
                for(int i=LOG-1;i>=0;--i) if(dis[fa[i][y]]>=dis[x]) y=fa[i][y]; 
            }
            if(x==y) return x;
            for(int i=LOG-1;i>=0;--i)  if(fa[i][x]!=fa[i][y]) x=fa[i][x], y=fa[i][y];  
            return fa[0][x];     
        }   
        #undef N
        #undef LOG
    };     
       
    // 优美的后缀树
       
    namespace tree
    {
        #define N 600003
        int tot,iii;     
        int son[N],size[N],dep[N],dfn[N],top[N],fa[N],up[N],down[N],val[N],idx[N],pp[N];       
        vector<int>G[N];    
        struct node
        {
            int u,c;
            node(int u=0,int c=0):u(u),c(c){}
        };
        vector<node>A[N]; 
        inline void add(int u,int v)
        {
            G[u].push_back(v);
            G[v].push_back(u);
        }
        void dfs1(int u,int ff)
        {
            fa[u]=ff,dep[u]=dep[ff]+1,size[u]=1;
            for(int i=0;i<G[u].size();++i)
            {
                if(G[u][i]==ff) continue;
                dfs1(G[u][i],u);
                size[u]+=size[G[u][i]];
                if(size[G[u][i]]>size[son[u]]) son[u]=G[u][i];    
            }
        }
        void dfs2(int u,int tp)
        { 
            idx[u]=++iii;   
            pp[iii]=u;         
            top[u]=tp;
            if(tp==u) dfn[tp]=++tot;                                 
            A[dfn[tp]].push_back(node(u,val[u])); 
            if(son[u]) dfs2(son[u],tp);
            for(int i=0;i<G[u].size();++i)
                if(G[u][i]!=son[u]&&G[u][i]!=fa[u])
                    dfs2(G[u][i], G[u][i]);                            
        }   
        inline void build()
        {   
            int i,j;
            dfs1(1,0);
            dfs2(1,1);   
            for(i=1;i<=tot;++i)   
            {
                sam::last=1; 
                // 从上到下 (实际是从下到上)      
                for(j=0;j<A[i].size();++j)
                {  
                    int u=A[i][j].u;
                    int c=A[i][j].c;  
                    up[u]=sam::extend(c);         
                }
                // 从下到上 (实际上是从上到下)
                sam::last=1;
                for(j=A[i].size()-1;j>=0;--j)
                {
                    int u=A[i][j].u;
                    int c=A[i][j].c;
                    down[u]=sam::extend(c);  
                }
            }
        }
        int LCA(int x,int y)
        {
            while(top[x]!=top[y]) dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]; 
            return dep[x]<dep[y]?x:y;
        }
        #undef N
    };
        
    // 优美的树链剖分
        
    struct node
    {
        int u,len,ty,tt;          
        node(int u=0,int len=0,int ty=0,int tt=0):u(u),len(len),ty(ty),tt(tt){}
    };
    #define N 300003 
    char S[N];
    int ty; 
    vector<node>A[2];  
    stack<node>ss;  
    void insert(int a,int b,int id)
    {
        int lca=tree::LCA(a,b);            
        // 向上走  
        while(tree::dep[a]>tree::dep[lca])
        {    
            int pp=tree::dep[tree::top[a]]<tree::dep[lca]?lca:tree::top[a]; 
            A[id].push_back(node(tree::up[a], tree::dep[a]-max(tree::dep[lca],tree::dep[tree::top[a]])+1,0,a)); 
            a=tree::fa[tree::top[a]];      
        }  
        // 向下走        
        if(a==lca)
        {
            // 未计算 lca       
            while(tree::dep[b]>=tree::dep[lca])
            {   
                int pp=tree::dep[tree::top[b]]<tree::dep[lca]?lca:tree::top[b]; 
                ss.push(node(tree::down[pp], tree::dep[b]-max(tree::dep[lca],tree::dep[pp])+1,1,pp));
                b=tree::fa[tree::top[b]];                                    
            }
        }
        else
        {   
            while(tree::dep[b]>tree::dep[lca])                                 
            {
                int pp=tree::dep[tree::top[b]]<tree::dep[lca]?lca:tree::top[b]; 
                ss.push(node(tree::down[pp], tree::dep[b]-max(tree::dep[lca],tree::dep[pp])+1,1,pp));
                b=tree::fa[tree::top[b]];
            }   
        }        
        while(!ss.empty()) A[id].push_back(ss.top()),ss.pop();     
    }         
    int main()
    {
        // setIO("input");  
        int i,j,n,m;     
        scanf("%d%s",&n,S+1);       
        for(i=1;i<=n;++i) tree::val[i]=S[i]-'a';     
        for(i=1;i<n;++i)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            tree::add(u,v);
        }
        sam::tot=1;  
        tree::build();              // 建完树剖  +  上/下走定位到后缀自动机的位置
        suffix_tree::build();       // 建完后缀树             
        scanf("%d",&m);
        for(i=1;i<=m;++i)
        {
            int a,b,c,d;        
            scanf("%d%d%d%d",&a,&b,&c,&d);        
            insert(a,b,0); 
            insert(c,d,1);                      
            int t1=0,t2=0,lcp=0;    
            while(t1<A[0].size()&&t2<A[1].size())
            {
                node a1=A[0][t1], a2=A[1][t2];      
                int lca;             
                int uu=a1.u;          
                int vv=a2.u;                              
                int u=a1.tt; 
                int v=a2.tt;     
                int tmp=min(sam::mx[lca=suffix_tree::LCA(uu,vv)], min(a1.len,a2.len));   
                lcp+=tmp;                                                                                
                if(tmp<min(a1.len,a2.len)) break;
                else
                {     
                    if(tmp==a1.len) ++t1;
                    else
                    {   
                        A[0][t1].u=(A[0][t1].ty==1)?(tree::down[tree::pp[tree::idx[u]+tmp]]):(tree::up[tree::pp[tree::idx[u]-tmp]]);         
                        A[0][t1].tt=(A[0][t1].ty==1)?(tree::pp[tree::idx[u]+tmp]):(tree::pp[tree::idx[u]-tmp]);
                        A[0][t1].len=A[0][t1].len-tmp; 
                    }
                    if(tmp==a2.len) ++t2;
                    else
                    {
                        A[1][t2].u=(A[1][t2].ty==1)?(tree::down[tree::pp[tree::idx[v]+tmp]]):(tree::up[tree::pp[tree::idx[v]-tmp]]);     
                        A[1][t2].tt=(A[1][t2].ty==1)?(tree::pp[tree::idx[v]+tmp]):(tree::pp[tree::idx[v]-tmp]);
                        A[1][t2].len=A[1][t2].len-tmp;
                    }  
                }
            }        
            A[0].clear(),A[1].clear();                
            printf("%d
    ",lcp);       
        }
        return 0;
    } 
    

      

  • 相关阅读:
    后端——框架——容器框架——spring_boot——《官网》阅读笔记——第四章节4(集成Web)——待补充
    后端——框架——容器框架——spring_boot——《官网》阅读笔记——第四章节5(集成安全框架)——待补充
    后端——框架——容器框架——spring_boot——《官网》阅读笔记——第四章节6(数据库)
    后端——框架——容器框架——spring_boot——《官网》阅读笔记——第四章节7(校验功能)——待补充
    其他(Excel函数集团)
    MM函数(Excel函数集团)
    Offset函数(Excel函数集团)
    Lookup函数(Excel函数集团)
    Indirect函数(Excel函数集团)
    查找与引用函数(Excel函数集团)
  • 原文地址:https://www.cnblogs.com/guangheli/p/11722379.html
Copyright © 2011-2022 走看看