zoukankan      html  css  js  c++  java
  • bzoj 4817: [Sdoi2017]树点涂色

    Description

    Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
    径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
    1 x:
    把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
    2 x y:
    求x到y的路径的权值。
    3 x y:
    在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
    Bob一共会进行m次操作

    Input

    第一行两个数n,m。
    接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
    接下来m行,表示操作,格式见题目描述
    1<=n,m<=100000 

    Output

    每当出现2,3操作,输出一行。
    如果是2操作,输出一个数表示路径的权值
    如果是3操作,输出一个数表示权值的最大值

    Sample Input

    5 6
    1 2
    2 3
    3 4
    3 5
    2 4 5
    3 3
    1 4
    2 4 5
    1 5
    2 4 5

    Sample Output

    3
    4
    2
    2

    HINT 

    Source

    鸣谢infinityedge上传

    这个题真的是醉得不行。。。

    考虑到第一个操作很烦,但是我们可以用LCT的access来解决这一操作。。。

    我们把这个点access的时候,把当前点的原来的重儿子所在的子树权值+1,把新接上来的重儿子的子树的权值-1。。。

    (这个直接用线段树来实现。。。)

    考虑到每次是染上一个未出现的颜色,可以画一下图来思考这样做的正确性:

    只有原来的重儿子的子树的val值要改变(+1),其余儿子的val值是不变(只是换了一种别的颜色而已,总数不变)。。。

    而新接上的重儿子的子树内因为少了当前点的颜色而需要-1(原来当前点和新重儿子是不同色的。。。)

    注意这些修改都是找到深度最小的点的子树来修改。。。

    然后对于第二个操作的查询,就是 查询val[u]+val[v]-2*val[lca(u,v)]+1。。。(因为u,v的颜色不相同,分lca的颜色讨论一下)

    // MADE BY QT666
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=300050;
    int n,m;
    struct tree{
      int head[N],to[N],cnt,nxt[N],size[N],son[N],fa[N],top[N],dfn[N],ed[N],tt,deep[N];
      void lnk(int x,int y){
        to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;
        to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt;
      }
      void dfs1(int x,int f){
        size[x]=1;deep[x]=deep[f]+1;
        for(int i=head[x];i;i=nxt[i]){
          int y=to[i];
          if(y!=f){
        fa[y]=x;dfs1(y,x);
        size[x]+=size[y];
        if(size[y]>size[son[x]]) son[x]=y; 
          }
        }
      }
      void dfs2(int x,int ff){
        top[x]=ff;dfn[x]=++tt;
        if(son[x]) dfs2(son[x],ff);
        for(int i=head[x];i;i=nxt[i]){
          int y=to[i];
          if(y!=son[x]&&y!=fa[x]) dfs2(y,y);
        }
        ed[x]=tt;
      }
      int lca(int x,int y){
        while(top[x]!=top[y]){
          if(deep[top[x]]<deep[top[y]]) swap(x,y);
          x=fa[top[x]];
        }
        if(deep[x]<deep[y]) swap(x,y);
        return y;
      }
    }Tree;
    struct segment_tree{
      int rt,Max[N],lazy[N],sz,ls[N],rs[N];
      void insert(int &x,int l,int r,int v,int d){
        if(!x) x=++sz;
        if(l==r){Max[x]=d;return;}
        int mid=(l+r)>>1;
        if(v<=mid) insert(ls[x],l,mid,v,d);
        else insert(rs[x],mid+1,r,v,d);
        Max[x]=max(Max[ls[x]],Max[rs[x]]);
      }
      void update(int x,int l,int r,int xl,int xr,int tag){
        if(xl<=l&&r<=xr){
          Max[x]+=tag;lazy[x]+=tag;return;
        }
        int mid=(l+r)>>1;
        if(xr<=mid) update(ls[x],l,mid,xl,xr,tag);
        else if(xl>mid) update(rs[x],mid+1,r,xl,xr,tag);
        else update(ls[x],l,mid,xl,mid,tag),update(rs[x],mid+1,r,mid+1,xr,tag);
        Max[x]=max(Max[ls[x]],Max[rs[x]])+lazy[x];
      }
      int query(int x,int l,int r,int xl,int xr,int la){
        if(xl<=l&&r<=xr) return Max[x]+la;
        int mid=(l+r)>>1;la+=lazy[x];
        if(xr<=mid) return query(ls[x],l,mid,xl,xr,la);
        else if(xl>mid) return query(rs[x],mid+1,r,xl,xr,la);
        else return max(query(ls[x],l,mid,xl,mid,la),query(rs[x],mid+1,r,mid+1,xr,la));
      }
    }seg;
    struct link_cut_tree{
      int c[N][2],fa[N];
      bool isroot(int x){
        return c[fa[x]][0]!=x && c[fa[x]][1]!=x;
      }
      void rotate(int x){
        int y=fa[x],z=fa[y],l,r;
        if(c[y][0]==x)l=0;else l=1;r=l^1;
        if(!isroot(y)){
            if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;
          }
        fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
        c[y][l]=c[x][r];c[x][r]=y;
      }
      void splay(int x){
        while(!isroot(x)){
        int y=fa[x],z=fa[y];
        if(!isroot(y)){
            if((c[y][0]==x)^(c[z][0]==y)) rotate(x);
            else rotate(y);
          }
        rotate(x);
          }
      }
      void access(int x){
        int t=0;
        while(x){
          splay(x);
          if(c[x][1]){
        int y=c[x][1];
        while(c[y][0]) y=c[y][0];
        seg.update(seg.rt,1,n,Tree.dfn[y],Tree.ed[y],1);
          }
          if(t){
        int y=t;
        while(c[y][0]) y=c[y][0];
        seg.update(seg.rt,1,n,Tree.dfn[y],Tree.ed[y],-1);
          }
          c[x][1]=t;t=x;x=fa[x];
        }
      }
    }LCT;
    int main(){
      scanf("%d%d",&n,&m);
      for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        Tree.lnk(x,y);
      }
      Tree.dfs1(1,0);Tree.dfs2(1,1);
      for(int i=1;i<=n;i++) seg.insert(seg.rt,1,n,Tree.dfn[i],Tree.deep[i]);
      for(int i=1;i<=n;i++) LCT.fa[i]=Tree.fa[i];
      for(int i=1;i<=m;i++){
        int type;scanf("%d",&type);
        if(type==1){
          int x;scanf("%d",&x);LCT.access(x);
        }
        if(type==2){
          int x,y;scanf("%d%d",&x,&y);
          int Lca=Tree.lca(x,y);
          x=Tree.dfn[x],y=Tree.dfn[y],Lca=Tree.dfn[Lca];
          printf("%d
    ",seg.query(seg.rt,1,n,x,x,0)+seg.query(seg.rt,1,n,y,y,0)-2*seg.query(seg.rt,1,n,Lca,Lca,0)+1);
        }
        if(type==3){
          int x;scanf("%d",&x);
          printf("%d
    ",seg.query(seg.rt,1,n,Tree.dfn[x],Tree.ed[x],0));
        }
      }
      return 0;
    }
    

      

    然后对于第三个操作就是查询子树最大值。。。

  • 相关阅读:
    网络IO
    进程与线程
    计算机网络
    操作系统
    刷题笔记
    fasd
    线程池
    epoll反应堆
    read函数
    dup与dup2
  • 原文地址:https://www.cnblogs.com/qt666/p/7268383.html
Copyright © 2011-2022 走看看