【ZJOI2018】历史

【ZJOI2018】历史

该来的总归还是会来的……

题意:

题目传送门

题解:

考虑把题意转化成一个更加科学一点的模型,发现这个崛起操作类似于\(LCT\)\(Access\)操作,继续分析一下,发现每次崛起的灾难度就是这次\(Access\)的轻重链的切换次数。那么题目就转化成了给出一个\(Access\)的顺序,让操作完之后的轻重链切换次数最大。

接下来考虑什么时候会造成轻重链的切换。显然,对于一个点\(x\),如果\(x\)所引出的重链发生了改变,那么说明来自\(x\)的两个不同子树中的节点进行了\(Access\)操作。

另一个容易发现的的结论就是每个点对于答案的贡献是独立的,所以我们可以单独计算每个点的贡献。我们记\(Ans_x\)表示\(x\)节点处轻重链切换次数的最大值,那么答案就是\(\sum_x Ans_x\)

接下来考虑如何计算一个点轻重链切换次数的最大值,我们记\(A_v\)表示\(v(v \in son_x)\)这个子树中的\(Access\)次数。我们实际上可以将问题的模型进行再次转化,对于一个点\(x\),我们把\(x\)子树中的所有\(Access\)操作看成一个小球,在\(x\)同一个孩子节点的子树下的球颜色一样,现在需要安排一个球的摆放顺序,使得相邻两个球颜色不同的数量最多。

考虑一种贪心构造的方法,我们记\(Mx_x\)表示\(x\)的孩子节点中\(A\)最大的值,如果\(Mx_x * 2 > A_x + 1\),那么答案就是\(2 * (A_x - Mx_x)\),否则答案就是\(A_x - 1\)。这个是容易证明的,我们尝试把其他颜色的球放在颜色最多的球的间隔中,如果放的满,那么此时其余颜色的球最少为\(Mx_x - 1\),答案就是球的个数减一,即\(A_x - 1\),否则说明一些颜色最多的球会放在一起,那么答案就是其余颜色球的个数乘以\(2\),即\(2 * (A_x - Mx_x)\)

于是我们现在就有了一个\(O(n)\)计算贡献的方法了。考虑如何优化。

对于一个点\(u\),如果某个点\(v\)\(A_v * 2 > A_u + 1\),那么我们就记\(u\)\(v\)的边为重边,否则为轻边,这样我们的答案可以简单的计算了,如果\(u\)没有重边,那么答案就是\(A_x - 1\),否则就是\(2 * (A_x - A_{heavyson})\)

我们发现,对于一次修改,影响到的就是\(x\)到根的一条链。对于经过的边,我们分情况讨论:

如果经过的边是重边,由于每次加上的一定是一个正整数,所以原本是重边的边仍然是重边,并且答案不变,因为\(A_x\)\(Mx_x\)都变大了\(w_i\)

如果经过的边是轻边,那么这次修改操作可能会让它变成重边,那么我们判断其是否会变成重边,然后重新计算一下答案即可。

为什么这样复杂度是正确的呢?考虑每经过一条轻边,\(A\)的值至少会乘以\(2\),那么轻边的个数最多为\(log\)值域 级别的。所以每次修改只会修改\(log\)次。

至于如何维护这棵树呢?一种方法是使用\(LCT\),稍微不同的是\(Access\)操作的时候不是将到根的所有边都设置成重边,而是按照上述的方法判断是否切换。然后复杂度分析仍然是按照\(LCT\)的分析,还是一样的。

Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 50;
typedef long long ll;

int n, m, tot;
ll a[N], res[N];
ll ans;
int heav[N], head[N];
struct edge {
  int to, nxt;
}E[N << 1];

namespace LCT {
  struct node {
    int ch[2];
    int fa;
    ll sum, tag;
  }t[N << 1];
#define ls(o) t[o].ch[0]
#define rs(o) t[o].ch[1]
#define pa(o) t[o].fa
  int Which(int o) { return rs(pa(o)) == o; }
  int Isroot(int o) { return ls(pa(o)) != o && rs(pa(o)) != o; }
  void Apply(int o, ll v) { t[o].sum += v; t[o].tag += v; }
  
  void Down(int o) {
    if(!o || !t[o].tag) return ;
    if(ls(o)) Apply(ls(o), t[o].tag);
    if(rs(o)) Apply(rs(o), t[o].tag);
    t[o].tag = 0;
  }
  
  void Rotate(int o) {
    int f = pa(o), ff = pa(f), c = Which(o);
    if(!Isroot(f)) t[ff].ch[Which(f)] = o; t[o].fa = ff;
    pa(t[o].ch[c ^ 1]) = f; t[f].ch[c] = t[o].ch[c ^ 1];
    t[o].ch[c ^ 1] = f; t[f].fa = o;
  }
  
  void TreeDown(int o) { if(!Isroot(o)) TreeDown(pa(o)); Down(o); }
  
  void Splay(int o) {
    TreeDown(o);
    for(; !Isroot(o); Rotate(o)) {
      if(!Isroot(pa(o))) Rotate( Which(o) ^ Which(pa(o)) ? o : pa(o));
    }
  }


  int Findroot(int o) {
    while(ls(o)) Down(o), o = ls(o);
    return o;
  }

  void Getans(int o) {
    ans -= res[o];
    res[o] = heav[o] ? (2ll * (t[o].sum - t[heav[o]].sum)) : (t[o].sum - 1);
    if(a[o] * 2 > t[o].sum + 1) res[o] = 2ll * (t[o].sum - a[o]);
    ans += res[o];
  }
  
  void Access(int o, ll v) {
    a[o] += v;
    for(int y = 0; o; y = o, o = pa(o)) {
      Splay(o);
      t[o].sum += v;
      if(ls(o)) Apply(ls(o), v);
      if(heav[o]) {
        TreeDown(heav[o]);
        if(t[heav[o]].sum * 2 <= t[o].sum + 1) heav[o] = 0, rs(o) = 0;
      }
      int v = Findroot(y);
      if(t[v].sum * 2 > t[o].sum + 1) heav[o] = v, rs(o) = v;
      Getans(o);
    }
  }
}
using namespace LCT;

void Addedge(int u, int v) {
  E[++tot].to = v; E[tot].nxt = head[u]; head[u] = tot;
}

void Dfs(int o, int fa) {
  t[o].fa = fa; t[o].sum = a[o];
  ll Mx = -1, pos;
  for(int i = head[o]; ~i; i = E[i].nxt) {
    int to = E[i].to;
    if(to == fa) continue;
    Dfs(to, o);
    t[o].sum += t[to].sum;
    if(t[to].sum > Mx) { Mx = t[to].sum; pos = to; }
  }
  if(Mx * 2 > t[o].sum + 1) heav[o] = pos;
  Getans(o);
  rs(o) = heav[o];
}

int main() {
  memset(head, -1, sizeof head);
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
  for(int i = 1, u, v; i < n; i++) {
    scanf("%d%d", &u, &v);
    Addedge(u, v); Addedge(v, u);
  }
  Dfs(1, 0);
  printf("%lld\n", ans);
  for(int i = 1; i <= m; i++) {
    int x; ll y;
    scanf("%d%lld", &x, &y);
    Access(x, y);
    printf("%lld\n", ans);
  }
  return 0;
}
posted @ 2019-04-18 21:02  Apocrypha  阅读(362)  评论(0编辑  收藏  举报