hdu 5002 Tree (LCT)

Problem Description

You are given a tree with N nodes which are numbered by integers 1..N. Each node is associated with an integer as the weight.

Your task is to deal with M operations of 4 types:

1.Delete an edge (x, y) from the tree, and then add a new edge (a, b). We ensure that it still constitutes a tree after adding the new edge.

2.Given two nodes a and b in the tree, change the weights of all the nodes on the path connecting node a and b (including node a and b) to a particular value x.

3.Given two nodes a and b in the tree, increase the weights of all the nodes on the path connecting node a and b (including node a and b) by a particular value d.

4.Given two nodes a and b in the tree, compute the second largest weight on the path connecting node a and b (including node a and b), and the number of times this weight occurs on the path. Note that here we need the strict second largest weight. For instance, the strict second largest weight of {3, 5, 2, 5, 3} is 3.


Input

The first line contains an integer T (T<=3), which means there are T test cases in the input.

For each test case, the first line contains two integers N and M (N, M<=10^5). The second line contains N integers, and the i-th integer is the weight of the i-th node in the tree (their absolute values are not larger than 10^4).

In next N-1 lines, there are two integers a and b (1<=a, b<=N), which means there exists an edge connecting node a and b.

The next M lines describe the operations you have to deal with. In each line the first integer is c (1<=c<=4), which indicates the type of operation.

If c = 1, there are four integers x, y, a, b (1<= x, y, a, b <=N) after c.
If c = 2, there are three integers a, b, x (1<= a, b<=N, |x|<=10^4) after c.
If c = 3, there are three integers a, b, d (1<= a, b<=N, |d|<=10^4) after c.
If c = 4 (it is a query operation), there are two integers a, b (1<= a, b<=N) after c.

All these parameters have the same meaning as described in problem description.


Output

For each test case, first output “Case #x:”” (x means case ID) in a separate line.

For each query operation, output two values: the second largest weight and the number of times it occurs. If the weights of nodes on that path are all the same, just output “ALL SAME” (without quotes).


Sample Input

2
3 2
1 1 2
1 2
1 3
4 1 2
4 2 3
7 7
5 3 2 1 7 3 6
1 2
1 3
3 4
3 5
4 6
4 7
4 2 6
3 4 5 -1
4 5 7
1 3 4 2 4
4 3 6
2 3 6 5
4 3 6


Sample Output

Case #1:
ALL SAME
1 2
Case #2:
3 2
1 1
3 2
ALL SAME


Solution

题目大意就是让你维护一棵树,支持动态删边,加边,修改一条路径上的值(分为加一个数或改成一个数),维护树上路径的次大值及其个数,这个次大值可能没有,假如一条路径都一样的话。

很显然删边加边,路径查询和修改就是动态树的事情了。
关键在于以下几点:

①怎样维护次大值及其个数呢呢?我们发现这个是无法直接维护的(自己维护自己的),所以我们只好维护最大值及其个数,次大值及其个数,然后在合并左右儿子信息的时候分类讨论一下就可以了。

②有两个修改方式,怎样才能按次序不影响答案呢?我一开始是想在每次添加一个修改标记之前,就将这条路径上的之前的标记都 Down 下去,但是,我们在 Access Splay 操作的时候,是无法保证之前的标记都被清完的,如果硬要清完,时间复杂度会上去而且较为麻烦。但是如果直接将标记累积在一个点的话的,那么次序就成了问题。比如来了一个change标记和两个add标记,明显先change再两次add与先add再change再add是不同的。因为change会使原先的标记变得无效(包括翻转标记,不过这个不重要)。

所以我们必须确定两个操作的优先级,使它们遵循一个规则变化使得答案没有影响。原先做线段树上的乘法与加法标记时,我们的做法是记一个times和一个plus标记,然后用类于乘法分配律的方法统一维护这两个标记。这次由于标记是打在Splay树上,我们rev是帮当前节点做,而add与change要帮自己的儿子做,就是将自己的儿子的标记打好,值改好,这样当前点的信息就可以 Up 得到正确的值。于是我们要在这里确定add和change的次序。如果当前打了一个change标记,代表之前的add要被清掉了;如果当前是一个add标记,那么如果之前有change,就将之前的change+v,否则才将add+v,因为如果这样是等价的而且能避免add被以前的change消掉。讲到这里,我们每次下放标记的次序就是先下放add再下放change,这样保证add被传下去,同时change也能有条不紊的进行。总之我认为add是辅助change的。(不明白的话可以手画一下。)然后打标记的问题就至此解决了。

③你以为只注意上面的就可以AC了吗?naive。别忘了如果你开的是int的话,是要加一个小小的特判的,就是如果Smax是-oo就不给他加值,如果你开long long的话就可以不用管他,不过在最后判断的时候是否没有次大值就需要再设一个恰当的上界来比较一下(yjp的做法),本人是开int的。


Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 100010
#define oo 0x7fffffff

using namespace std;

int T;
int n, m, cnt;

struct Tnode{
    Tnode *son[2], *fa;
    int val, Fmax, Smax, add, change;
    int size, Fnum, Snum, parent;
    bool rev;
    int Get_d(){return fa->son[1] == this;}
    int Lsize(){return son[0] ? son[0]->size : 0;}
    int Rsize(){return son[1] ? son[1]->size : 0;}
    void Connect(Tnode *now, int d){(son[d] = now)->fa = this;}
    void Up(){
      size = Lsize() + Rsize() + 1;
      Fmax = val;
      Fnum = 1;
      Smax = -oo;
      Snum = 0;
      if(son[0]){
        if(son[0]->Fmax > Fmax){
          if(son[0]->Smax > Fmax){
            Smax = son[0]->Smax;
            Snum = son[0]->Snum;
          }
          else if(son[0]->Smax == Fmax){
            Smax = son[0]->Smax;
            Snum = son[0]->Snum + Fnum;
          }
          else{
            Smax = Fmax;
            Snum = Fnum;
          }
          Fmax = son[0]->Fmax;
          Fnum = son[0]->Fnum;
        }
        else if(son[0]->Fmax == Fmax){
          Fnum += son[0]->Fnum;
          if(son[0]->Smax > Smax){
            Smax = son[0]->Smax;
            Snum = son[0]->Snum;
          }
          else if(son[0]->Smax == Smax)  Snum += son[0]->Snum;
        }
        else{
          if(son[0]->Fmax > Smax){
            Smax = son[0]->Fmax;
            Snum = son[0]->Fnum;
          }
          else if(son[0]->Fmax == Smax)  Snum += son[0]->Fnum;
        }
      }

      if(son[1]){
        if(son[1]->Fmax > Fmax){
          if(son[1]->Smax > Fmax){
            Smax = son[1]->Smax;
            Snum = son[1]->Snum;
          }
          else if(son[1]->Smax == Fmax){
            Smax = son[1]->Smax;
            Snum = son[1]->Snum + Fnum;
          }
          else{
            Smax = Fmax;
            Snum = Fnum;
          }
          Fmax = son[1]->Fmax;
          Fnum = son[1]->Fnum;
        }
        else if(son[1]->Fmax == Fmax){
          Fnum += son[1]->Fnum;
          if(son[1]->Smax > Smax){
            Smax = son[1]->Smax;
            Snum = son[1]->Snum;
          } 
          else if(son[1]->Smax == Smax)  Snum += son[1]->Snum;
        }
        else{
          if(son[1]->Fmax > Smax){
            Smax = son[1]->Fmax;
            Snum = son[1]->Fnum;
          }
          else if(son[1]->Fmax == Smax)  Snum += son[1]->Fnum;
        }
      }
    }
    void Down(){
      if(rev){
        swap(son[0], son[1]);
        if(son[0])  son[0]->rev ^= 1;
        if(son[1])  son[1]->rev ^= 1;
        rev = false;
      }

      if(add){
        if(son[0]){
          son[0]->val += add;
          son[0]->Fmax += add;
          if(son[0]->Smax != -oo)  son[0]->Smax += add;
          if(son[0]->change != oo)  son[0]->change += add;
          else  son[0]->add += add;
        }
        if(son[1]){
          son[1]->val += add;
          son[1]->Fmax += add;
          if(son[1]->Smax != -oo)  son[1]->Smax += add;
          if(son[1]->change != oo)  son[1]->change += add;
          else  son[1]->add += add;
        }
        add = 0;
      }

      if(change != oo){
        if(son[0]){
          son[0]->Fmax = change;
          son[0]->Smax = -oo;
          son[0]->Fnum = son[0]->size;
          son[0]->Snum = 0;
          son[0]->val = change;
          son[0]->change = change;
          son[0]->add = 0;
        }
        if(son[1]){
          son[1]->val = change;
          son[1]->change = change;
          son[1]->Fmax = change;
          son[1]->Smax = -oo;
          son[1]->Fnum = son[1]->size;
          son[1]->Snum = 0;
          son[1]->add = 0;
        }
        change = oo;
      }
    }
}tree[N], *Node[N];


Tnode *NewTnode(){
    tree[cnt].son[0] = tree[cnt].son[1] = tree[cnt].fa = NULL;
    tree[cnt].parent = 0;
    tree[cnt].rev = false;
    tree[cnt].Snum = tree[cnt].Fnum = 0;
    tree[cnt].Fmax = tree[cnt].Smax = -oo;
    tree[cnt].add = tree[cnt].val = 0;
    tree[cnt].change = oo;
    tree[cnt].size = tree[cnt].Fnum = 1;
    return tree+cnt++;
}

void Zig(Tnode *now){
    Tnode *last = now->fa;
    int d = now->Get_d();
    if(now->son[!d])  last->Connect(now->son[!d], d);
    else  last->son[d] = NULL;
    if(last->fa)  last->fa->Connect(now, last->Get_d());
    else  now->fa = NULL;
    now->Connect(last, !d);
    last->Up();
    now->parent = last->parent;
    last->parent = 0;
}

void Splay(Tnode *now){
    Tnode *last;
    while(now->fa){
      last = now->fa;
      if(last->fa)  last->fa->Down();
      last->Down();  now->Down();
      if(last->fa)  (now->Get_d() ^ last->Get_d()) ? Zig(now) : Zig(last);
      Zig(now);
    }
    if(!now->fa)  now->Down();
    now->Up();
}

void Access(int x){
    Splay(Node[x]);
    if(Node[x]->son[1]){
      Node[x]->son[1]->parent = x;
      Node[x]->son[1]->fa = NULL;
      Node[x]->son[1] = NULL;
      Node[x]->Up();
    }

    int y = Node[x]->parent;
    while(y){
      Splay(Node[y]);
      if(Node[y]->son[1]){
        Node[y]->son[1]->parent = y;
        Node[y]->son[1]->fa = NULL;
        Node[y]->son[1] = NULL;
        Node[y]->Up();
      }
      Node[y]->Connect(Node[x], 1);
      Node[y]->Up();
      Node[x]->parent = 0;
      x = y;
      y = Node[x]->parent;
    }
}

void Evert(int x){
    Access(x);
    Splay(Node[x]);
    Node[x]->rev ^= 1;
}

void Link(int x, int y){
    Evert(x);
    Node[x]->parent = y;
}

void Cut(int x, int y){
    Evert(x);
    Access(y);
    Splay(Node[x]);
    Node[x]->son[1]->fa = NULL;
    Node[x]->son[1] = NULL;
    Node[x]->Up();
}

void Update(int x, int y, int v, int sign){
    Evert(x);
    Access(y);
    Splay(Node[x]);
    if(sign == 1){
      Node[x]->val += v;
      Node[x]->Fmax += v;
      if(Node[x]->Smax != -oo)  Node[x]->Smax += v;
      if(Node[x]->change != oo)  Node[x]->change += v;
      else  Node[x]->add += v;
    }
    else{
      Node[x]->add = 0;
      Node[x]->change = v;
      Node[x]->val = v;
      Node[x]->Fmax = v;
      Node[x]->Smax = -oo;
      Node[x]->Fnum = Node[x]->size;
      Node[y]->Snum = 0;
    }
}

Tnode *Query(int x, int y){
    Evert(x);
    Access(y);
    Splay(Node[x]);
    if(Node[x]->Smax == -oo)  return NULL;
    return Node[x];
}

int main(){

    freopen("hdu5002.in", "r", stdin);
    freopen("hdu5002.out", "w", stdout);

    scanf("%d", &T);

    for(int Case = 1; Case <= T; Case++){
      printf("Case #%d:\n", Case);
      cnt = 0;
      scanf("%d%d", &n, &m);
      for(int i = 1; i <= n; i++)  Node[i] = NewTnode();
      for(int i = 1; i <= n; i++){
        scanf("%d", &Node[i]->val);
        Node[i]->Fmax = Node[i]->val;
      }

      int a, b;
      for(int i = 1; i < n; i++){
        scanf("%d%d", &a, &b);
        Link(a, b);
      }

      int op, c, d;
      for(int i = 1; i <= m; i++){
        scanf("%d", &op);
        if(op == 1){
          scanf("%d%d%d%d", &a, &b, &c, &d);
          Cut(a, b);
          Link(c, d);
        }
        else if(op == 2){
          scanf("%d%d%d", &a, &b, &c);
          Update(a, b, c, 0);
        }
        else if(op == 3){
          scanf("%d%d%d", &a, &b, &c);
          Update(a, b, c, 1);
        }
        else{
          scanf("%d%d", &a, &b);
          Tnode *temp = Query(a, b);
          if(!temp)  printf("ALL SAME\n");
          else  printf("%d %d\n", temp->Smax, temp->Snum);
        }
      }
    }
    return 0;
}

这里写图片描述

“我穿越了无数条世界线,却看不到我要的未来。”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值