HDU 4010 Query on The Trees 点权LCT

题目:

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

题意:

给定一个有 n 个点的树,有以下四种操作:

  • 1 x y 如果 x y 不在同一棵树上,那么在它们中间新建一条边,把他们连接起来,否则操作非法

    • 2 x y 如果 x y 在同一棵树上,那么把 x 替换为树根并且把y y 的父亲之间的边删掉,否则操作非法
    • 3 w x y如果 x y 在同一棵树上,那么把 xy 路径上所有点的点权加上 w ,否则操作非法
    • 4 x y如果 x y 在同一棵树上,查询 x y 路径上的最大点权,否则操作非法
    • 注意,非法操作都输出 1 ,无论是不是查询

      思路:

      直接LCT啊。。。我好像老把数据结构写搓,写搓了之后还不会 debug ,真是一场灾难,搞得我怀疑人生。。。

      #include <bits/stdc++.h>
      
      using namespace std;
      
      const int N = 300000 + 10, INF = 0x3f3f3f3f;
      
      struct edge
      {
          int to, next;
      }g[N*2];
      
      int cnt, head[N];
      int son[N][2], fat[N], key[N], maxval[N], rev[N], lazy[N];
      int top, stk[N];
      
      void init()
      {
          cnt = 0;
          memset(head, -1, sizeof head);
          memset(son, 0, sizeof son);
          memset(key, 0, sizeof key);
          memset(fat, 0, sizeof fat);
          memset(rev, 0, sizeof rev);
          memset(lazy, 0, sizeof lazy);
          memset(maxval, 0, sizeof maxval);
      }
      void add_edge(int v, int u)
      {
          g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;
      }
      void dfs(int v, int fa)
      {
          fat[v] = fa;
          for(int i = head[v]; ~i; i = g[i].next)
          {
              int u = g[i].to;
              if(u == fa) continue;
              dfs(u, v);
          }
      }
      bool is_root(int x)
      {
          return son[fat[x]][0] != x && son[fat[x]][1] != x;
      }
      void push_up(int x)
      {
          maxval[x] = max(key[x], max(maxval[son[x][0]], maxval[son[x][1]]));
      }
      void push_down(int x)
      {
          if(lazy[x])
          {//注意下放lazy标记时,如果不判断x的左右儿子是否存在而直接下放,相当于是下放到0这个点上,树上有很多点儿子为空都连接0,就会干扰到这些点,导致出现错误。。。
              if(son[x][0])
              {
                  lazy[son[x][0]] += lazy[x];
                  key[son[x][0]] += lazy[x];
                  maxval[son[x][0]] += lazy[x];
              }
              if(son[x][1])
              {
                  lazy[son[x][1]] += lazy[x];
                  key[son[x][1]] += lazy[x];
                  maxval[son[x][1]] += lazy[x];
              }
              lazy[x] = 0;
          }
          if(rev[x])
          {//翻转标记不用判断左右儿子是否存在
              swap(son[x][0], son[x][1]);
              rev[son[x][0]] ^= 1, rev[son[x][1]] ^= 1;
              rev[x] ^= 1;
          }
      }
      void Rotate(int x)
      {
          int y = fat[x], p = son[y][0] == x;
          son[y][!p] = son[x][p], fat[son[x][p]] = y;
          if(! is_root(y)) son[fat[y]][son[fat[y]][1]==y] = x;
          fat[x] = fat[y];
          son[x][p] = y, fat[y] = x;
          push_up(y);
      }
      void splay(int x)
      {
          top = 0;
          stk[++top] = x;
          for(int i = x; !is_root(i); i = fat[i]) stk[++top] = fat[i];
          for(int i = top; i >= 1; i--) push_down(stk[i]);
          while(! is_root(x))
          {
              int y = fat[x], z = fat[y];
              if(is_root(y)) Rotate(x);
              else
              {
                  if((x == son[y][0]) ^ (y == son[z][0])) Rotate(x), Rotate(x);
                  else Rotate(y), Rotate(x);
              }
          }
          push_up(x);
      }
      void access(int x)
      {
          int y = 0;
          while(x)
          {
              splay(x);
              son[x][1] = y;
              push_up(x);
              y = x, x = fat[x];
          }
      }
      int find_root(int x)
      {
          access(x); splay(x);
          while(son[x][0]) x = son[x][0];
          return x;
      }
      bool check(int x, int y)
      {
          return find_root(x) == find_root(y);
      }
      void make_root(int x)
      {
          access(x); splay(x);
          rev[x] ^= 1;
      }
      void link(int x, int y)
      {
          if(check(x, y))
          {
              printf("-1\n"); return;
          }
          make_root(x); fat[x] = y;
      }
      void cut(int x, int y)
      {
          if(x == y || !check(x, y))
          {
              printf("-1\n"); return;
          }
          make_root(x);
          access(y); splay(y);
          son[y][0] = fat[son[y][0]] = 0;
          push_up(y);
      }
      void update(int x, int y, int v)
      {
          if(!check(x, y))
          {
              printf("-1\n"); return;
          }
          make_root(x);
          access(y); splay(y);
          lazy[y] += v, maxval[y] += v, key[y] += v;//此时y的右子树为空,所以可以这样更新
      }
      int query(int x, int y)
      {
          if(!check(x, y)) return -1;
          make_root(x);
          access(y); splay(y);
          return maxval[y];
      }
      int main()
      {
          int n, m, opt, x, y, z;
          while(~ scanf("%d", &n))
          {
              init();
              for(int i = 1; i <= n-1; i++)
              {
                  scanf("%d%d", &x, &y);
                  add_edge(x, y); add_edge(y, x);
              }
              for(int i = 1; i <= n; i++) scanf("%d", &key[i]), maxval[i] = key[i];
              dfs(1, 0);
              scanf("%d", &m);
              for(int i = 1; i <= m; i++)
              {
                  scanf("%d%d%d", &opt, &x, &y);
                  if(opt == 1) link(x, y);
                  else if(opt == 2) cut(x, y);
                  else if(opt == 3)
                  {
                      scanf("%d", &z);
                      update(y, z, x);
                  }
                  else printf("%d\n", query(x, y));
              }
              printf("\n");
          }
          return 0;
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值