HDU 4010 Query on The Trees (动态树)(Link-Cut-Tree)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4010

题意;

先给你一棵树,有 \(4\) 种操作:

1、如果 \(x\) 和 \(y\) 不在同一棵树上则在\(x-y\)连边.

2、如果 \(x\) 和 \(y\) 在同一棵树上并且 \(x!=y\) 则把 \(x\) 换为树根并把 \(y\) 和 \(y\) 的父亲分离.

3、如果 \(x\) 和 \(y\) 在同一棵树上则 \(x\) 到 \(y\) 的路径上所有的点权值\(+w\).

4、如果 \(x\) 和 \(y\) 在同一棵树上则输出 \(x\) 到 \(y\) 路径上的最大值.

题解:

需要支持连边和删边等操作的,一般是动态树。

先把初始化的树建好,再套个 \(LCT\) 就可以了。可以说很裸了....但要注意题目中的一些细节。然而我还是调了挺久的...

\(LCT\)的基本操作复杂度均摊是\(O(log^2n)\),不过常数可能大一点。

代码:

#include  <bits/stdc++.h>
using namespace std;
typedef long long ll;

// 动态树问题,即要求我们维护一个由若干棵子结点无序的有根树组成的森林的连通性问题。
// 要求这个数据结构支持对树的分割、合并,对某个点到它到根的路径的某些操作
// LCT的基本操作复杂度为均摊O(log^2n).

// http://acm.hdu.edu.cn/showproblem.php?pid=4010

const int N = 300100;
int n,m,val[N];
struct Link_Cut_Tree
{
    int top,son[N][2],fa[N],q[N],rev[N];
    int NodeVal[N]; // 每个点的权值
    int MAX[N];
    int ADD[N];
    void init()
    {
      for(int i=0;i<=n;i++) {
        son[i][0] = son[i][1] = 0;
      }
      top = 0;
      memset(son,0,sizeof(son));
      memset(fa,0,sizeof(fa));
      memset(q,0,sizeof(q));
      memset(rev,0,sizeof(rev));
      memset(NodeVal,0,sizeof(NodeVal));
      memset(MAX,0,sizeof(MAX));
      memset(ADD,0,sizeof(ADD));
    }
    inline void pushup(int x)  //上传Splay的最大值
    {
      MAX[x] = max(MAX[son[x][0]],MAX[son[x][1]]);
      MAX[x] = max(MAX[x],NodeVal[x]);
    }
    void add(int x, int v)
    {
      NodeVal[x] += v;
      MAX[x] += v;
      ADD[x] += v;
    }
    inline void pushdown(int x) //下放Splay的翻转标记
    {
        int l=son[x][0],r=son[x][1];
        if(rev[x]){
            rev[l]^=1;rev[r]^=1;rev[x]^=1;
            swap(son[x][0],son[x][1]);
        }
        if(ADD[x])
        {
          if(son[x][0]) {
            add(son[x][0],ADD[x]);
          }
          if(son[x][1]) {
            add(son[x][1],ADD[x]);
          }
          ADD[x] = 0;
        }
    }
    // 判断这是不是一条重路径的根,只要他的fa指针指向的节点的左右子树都不是他,
    // 证明此时这是一条虚边那么这就是一棵子树的根节点
    inline bool isroot(int x) {
      return !fa[x] || (son[fa[x]][0]!=x && son[fa[x]][1]!=x);
    }
    void rotate(int x) {
        int y=fa[x],z=fa[y],l,r;
        if(son[y][0]==x)l=0;
        else l=1;
        r=l^1;
        if(!isroot(y)) {
          if(son[z][0]==y) son[z][0]=x;
          else son[z][1]=x;
        }
        fa[x]=z; fa[y]=x;
        fa[son[x][r]]=y;
        son[y][l]=son[x][r]; son[x][r]=y;
        pushup(y); pushup(x);
    }
    // 提根
    void splay(int x) {
        top=1;q[top]=x;
        for(int i=x;!isroot(i);i=fa[i]) q[++top]=fa[i];
        for(int i=top;i;i--) pushdown(q[i]);
        while(!isroot(x)) {
            int y=fa[x],z=fa[y];
            if(!isroot(y)) {
                if((son[y][0]==x)^(son[z][0]==y)) rotate(x);
                else rotate(y);
            }
            rotate(x);
        }
    }
    // LCT核心:从当前的节点x向它所在的根节点连一条重路径,相当于把沿路的重路径全都断开,重新拉一条从x到根的重路径
    void access(int x) {
      for(int t=0;x;t=x,x=fa[x]) {
        splay(x);
        son[x][1]=t;
        pushup(x);
      }
    }
    //换根:让 x 成为当前树的根节点
    void makeroot(int x) {
      access(x);
      splay(x);
      rev[x]^=1;
    }
    // 先找到 x 所在的 auxiliary tree(即preferred path),并查找返回该条路径最小的节点(即根)
    int find_root(int x) {
      access(x);
      splay(x);
      while(son[x][0]) x=son[x][0];
      return x;
    }
    //获取到 x?y所对应的路径,可以通过访问y节点来获取到有关路径的信息
    void split(int x,int y) {
      makeroot(x);
      access(y);
      splay(y);
    }
    // 断开 x 和 y 之间的边
    void cut(int x,int y) {
      split(x,y);
     // if(son[y][0]==x && son[x][1]==0)
     // {
     //   son[y][0]=0, fa[x]=0;
     //   pushup(y);
     // }
     fa[son[y][0]]=0;son[y][0]=0;
     pushup(y);
    }
    // 在 x 和 y 之间连边
    void link(int x,int y) {
      makeroot(x);
      // 注意有些题不保证x,y未联通,还需要判断一下联通性
      // if(find_root(y)==x) return; //两点已经在同一子树中,再连边不合法
      fa[x]=y;
    }
    // 将x-y路径上的权值都加 w
    void add_val(int x,int y,int w)
    {
      split(x,y);
      add(y,w);
    }
    // 输出x-y之间的最大点权
    int query(int x, int y)
    {
      split(x,y);
      return MAX[y];
    }
}LCT;
std::vector<int> ve[N];
void dfs(int u, int fa)
{
  LCT.fa[u] = fa;
  for(int i = 0; i < (int)ve[u].size(); i++){
    int v = ve[u][i];
    if(v == fa)continue;
    dfs(v,u);
  }
}
int main(int argc, char const *argv[]) {
  // freopen("in.txt","r",stdin);
  while(std::cin >> n) {

    memset(val,0,sizeof(val));
    for(int i = 0; i <= n; i++) ve[i].clear();
    LCT.init();
    for(int i = 1; i < n; i++) {
      int u,v;
      scanf("%d%d",&u,&v);
      // LCT.link(u,v);
      ve[u].push_back(v);
      ve[v].push_back(u);
    }
    dfs(1,0);
    for(int i = 1; i <= n; i++) {
      int x;
      scanf("%d", &x);
      LCT.NodeVal[i] = x;
      LCT.MAX[i] = x;
    }
    std::cin >> m;
    for(int i = 1; i <= m; i++) {
      int op, x, y;
      scanf("%d", &op);
      // 连接x到 y,若x到 y已经联通则无需连接
      if(op == 1) {
        scanf("%d%d",&x,&y);
        int xx = LCT.find_root(x), yy = LCT.find_root(y);
        if(xx != yy) LCT.link(x,y);
        else {
          puts("-1");
        }
      }
      // 删除边(x,y),但不保证边(x,y)存在
      else if(op == 2)
      {
        scanf("%d%d",&x,&y);
        int xx = LCT.find_root(x), yy = LCT.find_root(y);
        if(xx == yy && x != y) LCT.cut(x,y);
        else {
          puts("-1");
        }
      }
      // 将x-y路径上的权值都加 w
      else if(op == 3) {
        int w;
        scanf("%d%d%d",&w,&x,&y);
        // std::cout << "w = " << w << '\n';
        if(LCT.find_root(x) == LCT.find_root(y)) LCT.add_val(x,y,w);
        else puts("-1");

      }
      // 输出x-y之间的最大点权
      else if(op == 4) {
        scanf("%d%d",&x,&y);
        if(LCT.find_root(x) == LCT.find_root(y)) printf("%d\n",LCT.query(x,y));
        else {
          puts("-1");
        }
      }
    }
    puts("");
  }

  return 0;
}

原文地址:https://www.cnblogs.com/LzyRapx/p/9174595.html

时间: 2024-10-06 20:08:43

HDU 4010 Query on The Trees (动态树)(Link-Cut-Tree)的相关文章

HDU 4010 Query on The Trees(动态树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4010 题意:一棵树,四种操作: (1)若x和y不在一棵树上,将x和y连边: (2)若x和y在一棵树上,将x变成树根,将y从x树上分离: (3)若x和y在一棵树上,将x到y路径上的所有值增加det: (4)若x和y在一棵树上,输出x到y路径上的最大值. 思路:1操作用link维护,2操作用cut,34操作先split(x,y),然后对y做tag,并且记录路径的max值. 1 #include<iost

hdu 4010 Query on The Trees(动态树)

题意:给定一幅图的连接情况,给出每个点的权值,四种操作: 1 x y 连接x.y所在子树: 2 x y 将同一棵树上的x,y分离,形成两棵子树: 3 w x y 将x.y之间路径上的所有点权加w: 4 x y 查询x.y路径上点权的最大值: 动态树学习参考:http://www.cnblogs.com/BLADEVIL/p/3510997.html http://wenku.baidu.com/view/75906f160b4e767f5acfcedb http://m.blog.csdn.ne

HDU 4010.Query on The Trees 解题报告

题意: 给出一颗树,有4种操作: 1.如果x和y不在同一棵树上则在xy连边 2.如果x和y在同一棵树上并且x!=y则把x换为树根并把y和y的父亲分离 3.如果x和y在同一棵树上则x到y的路径上所有的点权值+w 4.如果x和y在同一棵树上则输出x到y路径上的最大值 动态树入门题: #include <iostream> #include <cstdio> using namespace std; const int MAXN = 333333; struct node { int v

HDU 4010 Query on The Trees

题意: 一棵树  支持合并.分离.路径加权值.路径权值最大值 思路: LCT入门题  也是我的第一道-  代码来源于kuangbin巨巨  我只是整理出自己的风格留作模版- LCT比较好的入门资料是--<QTREE解法的一些研究> LCT基本做法就是先dfs建树  然后根据输入做上述4个操作 对于合并  就是把u转到树根  然后接在v上 对于分离  就是把u转到splay的根  然后切断与左子树的连接 对于路径加值  就是求出lca  然后包含u和v的子树以及lca点进行加值 对于路径求最值 

hdu 4010 Query on the trees LCT

维护一个带点权的无向图森林,支持: 1.删边 2.加边 3.增加某条链所有点的点权 4.求某条链上点权的最大值 大概思路: 前两个需要更改根(即需要翻转标记) 第三个又需要一个标记,第四个每个节点记录该splay中以该节点为根的子树的最大点权. 收获: 1.对于splay前的标记下传可用递归写法, 虽然慢一点,但不容易写错. 2.access的过程中,在边转换完成后要更新节点信息. 3.对于寻找某子树的根,可以一直向上(会途径重边和轻边)找,这样比access+splay快很多,也很好写. 4.

P3690 【模板】Link Cut Tree (动态树)

P3690 [模板]Link Cut Tree (动态树) https://www.luogu.org/problemnew/show/P3690 分析: LCT模板 代码: 注意一下cut! 1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 6 const int N = 300100; 7 8 int val[N],fa[N],ch[N][2],rev[N],sum[N],st[N],top;

动态树之link-cut tree

说好的专题... lct的一些概念看论文 杨哲<QTREE解法的一些研究> 简单易懂. 首先不要把lct想象得很难,其实很水的.lct就是很多splay树维护的树... lct的access操作就是在原树中拓展一条点到根的类二叉树出来(用splay来维护) 这里,splay树是按深度作为关键字的,当然,在无向图中(无环)可以任意指定一个点为根,这点要切记(因为在这里操作时,有些操作需要换根,所以一定要理解) link操作就是将点作为这颗类二叉树的根,然后合并 cut操作就是将点作为这颗二叉树的

LuoguP3690 【模板】Link Cut Tree (动态树) LCT模板

P3690 [模板]Link Cut Tree (动态树) 题目背景 动态树 题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和.保证x到y是联通的. 1:后接两个整数(x,y),代表连接x到y,若x到y已经联通则无需连接. 2:后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在. 3:后接两个整数(x,y),代表将点x上的权值变成y. 输入输出

HDOJ 4010 Query on The Trees LCT

LCT: 分割.合并子树,路径上全部点的点权添加一个值,查询路径上点权的最大值 Query on The Trees Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others) Total Submission(s): 2582    Accepted Submission(s): 1208 Problem Description We have met so many problems