[ HEOI 2016 ] 樹

\(\\\)node

Description


給出一顆樹,開始只有 \(1\) 號節點有標記。ios

  • \(\ C\ x\)\(x\) 號節點打標記c++

  • \(\ Q\ x\) 查詢 \(x\) 號節點深度最深的有標記的祖先git

\(\\\)優化

Solution


  • 鏈剖作法:ui

    查詢直到跳到第一個有權的重鏈上,線段樹上二分便可。太板了不說了。spa

  • DFS序+線段樹作法:指針

    一遍DFS求出DFS序,子樹大小以及節點深度。code

    用線段樹維護DFS序,每一個節點記錄覆蓋當前區間深度最深的節點編號。標記下放的時候只需選擇深度更深的做爲答案便可。注意設置根節點的深度,不然第一次的全局標記可能會無效。遞歸

    由於DFS序中一棵子樹是連續的,因此標記能夠看做整棵子樹的區間覆蓋操做。查詢也很方便,在遞歸查找時下放標記便可。

    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 100010
    #define gc getchar
    #define Rg register
    #define mid ((l+r)>>1)
    using namespace std;
    
    inline int rd(){
      int x=0; bool f=0; char c=gc();
      while(!isdigit(c)){if(c=='-')f=1;c=gc();}
      while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
      return f?-x:x;
    }
    
    int n,m,tot,hd[N];
    
    int cnt,s[N],d[N],dfn[N],sz[N];
    
    struct edge{int to,nxt;}e[N];
    
    inline void add(int u,int v){
      e[++tot].to=v; e[tot].nxt=hd[u]; hd[u]=tot;
    }
    
    void dfs(int u,int fa){
      dfn[u]=++cnt;
      s[cnt]=u; sz[u]=1;
      for(Rg int i=hd[u],v;i;i=e[i].nxt)
        if((v=e[i].to)!=fa){
          d[v]=d[u]+1;
          dfs(v,u);
          sz[u]+=sz[v];
        }
    }
    
    struct segment{
    
      int root,ptr;
    
      inline int newnode(){return ++ptr;}
    
      struct node{int ls,rs,tag;}c[N<<1];
    
      inline void build(int &rt,int l,int r){
        rt=newnode();
        if(l==r) return;
        build(c[rt].ls,l,mid);
        build(c[rt].rs,mid+1,r);
      }
    
      inline void pushdown(int rt){
        if(d[c[rt].tag]>d[c[c[rt].ls].tag]) c[c[rt].ls].tag=c[rt].tag;
        if(d[c[rt].tag]>d[c[c[rt].rs].tag]) c[c[rt].rs].tag=c[rt].tag;
        c[rt].tag=0;
      }
    
      inline void updata(int rt,int l,int r,int L,int R,int p){
        if(l>R||r<L) return;
        if(l>=L&&r<=R){
          if(d[p]>d[c[rt].tag]) c[rt].tag=p;
          return;
        }
        if(c[rt].tag) pushdown(rt);
        if(L<=mid) updata(c[rt].ls,l,mid,L,R,p);
        if(R>mid) updata(c[rt].rs,mid+1,r,L,R,p);
      }
    
      inline int query(int rt,int l,int r,int p){
        if(l==r) return c[rt].tag;
        if(c[rt].tag) pushdown(rt);
        if(p<=mid) return query(c[rt].ls,l,mid,p);
        else return query(c[rt].rs,mid+1,r,p);
      }
    
    }tree;
    
    int main(){
      n=rd(); m=rd();
      for(Rg int i=1,u,v;i<n;++i){
        u=rd(); v=rd();
        add(u,v); add(v,u);
      }
      d[1]=1; dfs(1,0);
      tree.build(tree.root,1,n);
      tree.updata(tree.root,1,n,1,n,1);
      char c; int x;
      while(m--){
        c=gc(); while(!isalpha(c)) c=gc();
        if(c=='Q') printf("%d\n",tree.query(tree.root,1,n,dfn[rd()]));
        else{x=rd();tree.updata(tree.root,1,n,dfn[x],dfn[x]+sz[x]-1,x);}
      }
      return 0;
    }
  • 並查集作法:

    咱們用並查集指針表明當前最近的標記節點的方向,開始都指向父節點。標記一個點只需直接將指針指向本身,查詢即找到集合表明元素。容易發現正着處理複雜度不對,由於要保證樹的形態正確,因此咱們不能使用並查集的優化方式。

    時光倒流。開始先把全部的標記打上,其他的點指向父節點。倒着模擬,遇到打標記就撤銷標記,詢問就是找到集合表明元素。能夠使用路徑壓縮優化,由於只會撤銷標記,任意時刻集合的表明元素必然是集合內的最優答案。

    注意一個節點可能被屢次打標記,在最後一次撤銷標記時咱們再將其與父節點 merge 在一塊兒。

    #include<cmath>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 100010
    #define R register
    #define gc getchar
    #define mid ((l+r)>>1)
    using namespace std;
    
    inline int rd(){
      int x=0; bool f=0; char c=gc();
      while(!isdigit(c)){if(c=='-')f=1;c=gc();}
      while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
      return f?-x:x;
    }
    
    int n,m,tot,hd[N],fa[N],cnt[N];
    
    struct edge{int to,nxt;}e[N<<1];
    
    inline void add(int u,int v){
      e[++tot].to=v; e[tot].nxt=hd[u]; hd[u]=tot;
    }
    
    void dfs(int u){
      for(R int i=hd[u],v;i;i=e[i].nxt)
        if((v=e[i].to)!=fa[u]){fa[v]=u;dfs(v);}
    }
    
    struct Q{int op,x,ans;}q[N];
    
    struct UFS{
      int f[N];
      UFS(){memset(f,0,sizeof(f));}
      int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
      inline void merge(int x,int fa){f[find(x)]=find(fa);}
    }ufs;
    
    int main(){
      n=rd(); m=rd();
      for(R int i=1,u,v;i<n;++i){
        u=rd(); v=rd();
        add(u,v); add(v,u);
      }
      fa[1]=1;
      dfs(1); char c;
      for(R int i=1;i<=m;++i){
        c=gc(); while(!isalpha(c)) c=gc();
        q[i].op=(c=='C'); q[i].x=rd();
        if(q[i].op){ufs.f[q[i].x]=q[i].x;++cnt[q[i].x];}
      }
      for(R int i=1;i<=n;++i) if(!ufs.f[i]) ufs.f[i]=fa[i];
      for(R int i=m;i;--i)
        if(q[i].op){
          --cnt[q[i].x];
          if(!cnt[q[i].x]) ufs.merge(q[i].x,fa[q[i].x]);
        }
        else q[i].ans=ufs.find(q[i].x);
      for(R int i=1;i<=m;++i) if(!q[i].op) printf("%d\n",q[i].ans);
      return 0;
    }
相關文章
相關標籤/搜索