zoukankan      html  css  js  c++  java
  • 点分治与动态点分治略解

    暑假的时候学的算法,太久不用就忘记了代码怎么写。

    点分治

    就大概的讲一下ba。

    将一棵无根树转化为以重心为根的有根树,假设为(p),那么对于树上的路径,就可以分为两类:

    1. 经过根节点(p)
    2. 包含于(p)的某一棵子树内(不经过根节点)

    由于重心的性质:

    以重心为根,任意一棵子树的大小都不超过整棵树大小的一半。

    所以保证了复杂度为(O(Nlog N))

    对于第一种路径直接进行统计,对于第二种路径,则删除与重心相连的所有边,然后分治下去。

    需要注意的是,代码里的solve函数应该根据实际情况进行编写,而不是直接套模板。

    #include <bits/stdc++.h>
    using namespace std;
    
    inline int ty() {
      char ch = getchar();
      int x = 0, f = 1;
      while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
      }
      while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
      }
      return x * f;
    }
    
    const int _ = 1e5 + 10;
    const int INF = 0x3f3f3f3f;
    int tot, head[_], to[_ << 1], nxt[_ << 1], edge[_ << 1];
    
    void adde(int x, int y, int z) {
      to[++tot] = y;
      edge[tot] = z;
      nxt[tot] = head[x];
      head[x] = tot;
    }
    
    int N, K;
    int root, totsiz, mxsiz, siz[_], vis[_], ans;
    int dis[_], cnt;
    
    void getroot(int x, int fa) {
      siz[x] = 1;
      int maxx = 0;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == fa) continue;
        getroot(y, x);
        siz[x] += siz[y];
        maxx = max(maxx, siz[y]);
      }
      maxx = max(maxx, totsiz - siz[x]);
      if (maxx < mxsiz) mxsiz = maxx, root = x;
    }
    
    void query(int x, int fa, int d) {
      dis[++cnt] = d;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i], z = edge[i];
        if (vis[y] || y == fa) continue;
        query(y, x, d + z);
      }
    }
    
    int solve(int x, int s) {
      cnt = 0;
      query(x, 0, s);
      sort(dis + 1, dis + cnt + 1);
      int l = 1, r = cnt, sum = 0;
      while (l < r) {
        if (dis[l] + dis[r] <= K)
          sum += r - l, ++l;
        else
          --r;
      }
      return sum;
    }
    
    void divide(int x) {
      ans += solve(x, 0), vis[x] = 1;
      int nowsiz = totsiz;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i], z = edge[i];
        if (vis[y]) continue;
        ans -= solve(y, z);
        totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
        mxsiz = INF, root = 0;
        getroot(y, 0);
        divide(root);
      }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("tree.in", "r", stdin);
      freopen("tree.out", "w", stdout);
    #endif
      N = ty();
      for (int i = 1; i < N; ++i) {
        int x = ty(), y = ty(), z = ty();
        adde(x, y, z);
        adde(y, x, z);
      }
      K = ty();
      totsiz = N, mxsiz = INF;
      getroot(1, 0);
      divide(root);
      printf("%d
    ", ans);
      return 0;
    }
    

    动态点分治

    好了,这才是今天的主角。

    点分树

    建立点分树只需要在点分治的基础上,加一行代码fa[root]=x

    void divide(int x) {
      vis[x] = 1;
      int nowsiz = totsiz;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i], z = edge[i];
        if (vis[y]) continue;
        mxsiz = INF, root = 0;
        totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
        getroot(y, 0);
        fa[root] = x;
        divide(root);
      }
    }
    

    实现修改

    修改的时候不断暴跳点分树即可。

    是的,你没看错,这就是动态点分治

    下面根据例题来具体解读

    [ZJOI2007]捉迷藏

    求出点分树,对于每个结点(x)维护两个 可删堆(dist_x)存储结点(x)代表的连通块中的所有黑点到(fa_x)的距离信息, (sub_x)表示结点(x)在点分树上的所有儿子和它自己中的黑点到(x)的距离信息,由于本题贪心的求答案方法,且两个来自于同一子树的路径不能成为一条完成的路径,我们只在这个堆中插入其自己的值和其每个子树中的最大值。我们发现,(sub_x)中最大的两个值的和就是分治时分支中心为(x)时经过结点(x)的最长黑端点路径。我们可以用全局可删堆(ans)存储所有结点的答案,这个堆中的最大值就是我们所求的答案。

    #include <bits/stdc++.h>
    using namespace std;
    
    namespace IO {
    const int maxn((1 << 21) + 1);
    
    char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, ch,
                                           st[55];
    int opt, tp;
    char Getc() {
      return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin),
                          (iS == iT ? EOF : *iS++))
                       : *iS++);
    }
    
    void Flush() {
      fwrite(obuf, 1, oS - obuf, stdout);
      oS = obuf;
    }
    
    void Putc(char x) {
      *oS++ = x;
      if (oS == oT) Flush();
    }
    
    template <class Int>
    void Input(Int &x) {
      for (opt = 1, ch = Getc(); ch < '0' || ch > '9'; ch = Getc())
        opt = ch == '-' ? -1 : 1;
      for (x = 0; ch <= '9' && ch >= '0'; ch = Getc())
        x = (x << 3) + (x << 1) + (ch ^ 48);
      x *= opt;
    }
    
    template <class Int>
    void Print(Int x) {
      if (!x) Putc('0');
      if (x < 0) Putc('-'), x = -x;
      while (x) st[++tp] = x % 10 + '0', x /= 10;
      while (tp) Putc(st[tp--]);
    }
    
    void Getstr(char *s) {
      for (ch = Getc(); ch < 'A' || ch > 'Z'; ch = Getc())
        ;
      for (; ch <= 'Z' && ch >= 'A'; ch = Getc()) *s++ = ch;
      *s = 0;
    }
    
    void Putstr(const char *s) {
      for (int i = 0, n = strlen(s); i < n; ++i) Putc(s[i]);
    }
    }  // namespace IO
    using IO::Flush;
    using IO::Getstr;
    using IO::Input;
    using IO::Print;
    using IO::Putc;
    
    const int _ = 1e5 + 10;
    const int INF = 0x3f3f3f3f;
    
    int tot, head[_], to[_ << 1], nxt[_ << 1];
    void adde(int x, int y) {
      to[++tot] = y;
      nxt[tot] = head[x];
      head[x] = tot;
    }
    
    struct Heap {
      priority_queue<int> data, del;
      void insert(int x) { data.push(x); }
      void erase(int x) { del.push(x); }
      void pop() {
        while (del.size() && data.top() == del.top()) data.pop(), del.pop();
        data.pop();
      }
      int top() {
        while (del.size() && data.top() == del.top()) data.pop(), del.pop();
        return data.top();
      }
      int top2() {
        int t = top(), ret;
        pop();
        ret = top();
        data.push(t);
        return ret;
      }
      int size() { return data.size() - del.size(); }
    } dist[_], sub[_], ans;
    
    int N, T;
    int siz[_], vis[_], fa[_], dep[_], mxsiz, totsiz, root;
    
    void getroot(int x, int f) {
      siz[x] = 1;
      int maxx = 0;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        getroot(y, x);
        siz[x] += siz[y];
        maxx = max(maxx, siz[y]);
      }
      maxx = max(maxx, totsiz - siz[x]);
      if (maxx < mxsiz) mxsiz = maxx, root = x;
    }
    
    void dfs(int x, int fa, int d, Heap &s) {
      s.insert(d);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == fa) continue;
        dfs(y, x, d + 1, s);
      }
    }
    
    void divide(int x) {
      vis[x] = 1;
      int nowsiz = totsiz;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y]) continue;
        mxsiz = INF, totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
        getroot(y, 0);
        fa[root] = x;
        dfs(y, x, 1, dist[root]);
        sub[x].insert(dist[root].top());
        dep[root] = dep[x] + 1;
        divide(root);
      }
      sub[x].insert(0);
      if (sub[x].size() >= 2)
        ans.insert(sub[x].top() + sub[x].top2());
      else if (sub[x].size())
        ans.insert(sub[x].top());
    }
    
    namespace lca {
    int top[_], dep[_], ssize[_], hson[_], fa[_];
    void dfs1(int u, int ff) {
      fa[u] = ff;
      ssize[u] = 1;
      dep[u] = dep[ff] + 1;
      for (int i = head[u]; i; i = nxt[i]) {
        int v = to[i];
        if (v == ff) continue;
        dfs1(v, u);
        ssize[u] += ssize[v];
        if (ssize[hson[u]] < ssize[v]) hson[u] = v;
      }
    }
    void dfs2(int u, int tp) {
      top[u] = tp;
      if (hson[u]) dfs2(hson[u], tp);
      for (int i = head[u]; i; i = nxt[i]) {
        int v = to[i];
        if (v == fa[u] || v == hson[u]) continue;
        dfs2(v, v);
      }
    }
    void init() {
      dfs1(1, 0);
      dfs2(1, 1);
    }
    int query(int u, int v) {
      while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        u = fa[top[u]];
      }
      return dep[u] < dep[v] ? u : v;
    }
    int dis(int u, int v) { return dep[u] + dep[v] - dep[query(u, v)] * 2; }
    }  // namespace lca
    
    int light[_], d[_][20];
    
    inline void turn_off(int x) {
      if (sub[x].size() >= 2) ans.erase(sub[x].top() + sub[x].top2());
      sub[x].insert(0);
      if (sub[x].size() >= 2) ans.insert(sub[x].top() + sub[x].top2());
      for (int i = x; fa[i]; i = fa[i]) {
        if (sub[fa[i]].size() >= 2) ans.erase(sub[fa[i]].top() + sub[fa[i]].top2());
        if (dist[i].size()) sub[fa[i]].erase(dist[i].top());
        dist[i].insert(d[x][dep[x] - dep[fa[i]]]);
        sub[fa[i]].insert(dist[i].top());
        if (sub[fa[i]].size() >= 2)
          ans.insert(sub[fa[i]].top() + sub[fa[i]].top2());
      }
    }
    
    inline void turn_on(int x) {
      if (sub[x].size() >= 2) ans.erase(sub[x].top() + sub[x].top2());
      sub[x].erase(0);
      if (sub[x].size() >= 2) ans.insert(sub[x].top() + sub[x].top2());
      for (int i = x; fa[i]; i = fa[i]) {
        if (sub[fa[i]].size() >= 2) ans.erase(sub[fa[i]].top() + sub[fa[i]].top2());
        sub[fa[i]].erase(dist[i].top());
        dist[i].erase(d[x][dep[x] - dep[fa[i]]]);
        if (dist[i].size()) sub[fa[i]].insert(dist[i].top());
        if (sub[fa[i]].size() >= 2)
          ans.insert(sub[fa[i]].top() + sub[fa[i]].top2());
      }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("hide.in", "r", stdin);
      freopen("hide.out", "w", stdout);
    #endif
      Input(N);
      for (int i = 1; i < N; ++i) {
        int x, y;
        Input(x), Input(y);
        adde(x, y);
        adde(y, x);
      }
      lca::init();
    
      mxsiz = INF, totsiz = N;
      getroot(1, 0);
      divide(root);
      // for (int i = 1; i <= N; ++i) printf("%d %d
    ", i, fa[i]);
      for (int i = 1; i <= N; ++i)
        for (int j = i; j; j = fa[j]) d[i][dep[i] - dep[j]] = lca::dis(i, j);
    
      Input(T);
      while (T--) {
        char op[3];
        Getstr(op);
        if (op[0] == 'G') {
          if (ans.size())
            Print(ans.top()), Putc('
    ');
          else
            Print(-1), Putc('
    ');
        } else {
          int x;
          Input(x);
          if (light[x])
            turn_off(x);
          else
            turn_on(x);
          light[x] ^= 1;
        }
      }
      return Flush(), 0;
    }
    

    BZOJ3730 震波

    Description

    给你一棵树,每个节点有点权,你需要执行以下两种操作:

    1. 查询与点(x)距离不超过(k)的所有点的点权和
    2. 将点(x)的点权修改为(y)

    强制在线。

    Solution

    还是仿照上一题的思路,将点分树建出来,然后对于每个节点,建立两个树状数组(dist_x,sub_x),下标为距离,权值为点权和。其中,(dist_x)表示(x)的联通块内,所有点到(x)的距离信息;(sub_x)表示(x)的联通块内,所有点到(x)在点分树上的父亲的距离信息。

    对于查询,不断暴跳父亲,然后容斥一下即可。

    对于修改,同样也是不断暴跳父亲,然后维护(dist_x,sub_x)

    需要注意的是在向上跳暴力修改的过程中,要判断节点(x)到当前节点的距离是否已经大于(y-len),且不合法时不是break而应该是continue,就这个我一开始用线段树写没判居然能过,改成树状数组后调了我好久。。。
    PS:不过这次调试过程中,我发现了又一法宝:assert,用这东西两下子就找出来了哪里出问题了!

    Code

    动态开点线段树(TLE)

    #include <bits/stdc++.h>
    using namespace std;
    
    bool xxxxx;
    
    namespace IO {
    const int maxn((1 << 21) + 1);
    char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, ch,
                                           st[55];
    int opt, tp;
    char Getc() {
      return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin),
                          (iS == iT ? EOF : *iS++))
                       : *iS++);
    }
    void Flush() {
      fwrite(obuf, 1, oS - obuf, stdout);
      oS = obuf;
    }
    void Putc(char x) {
      *oS++ = x;
      if (oS == oT) Flush();
    }
    template <class Int>
    void Input(Int &x) {
      for (opt = 1, ch = Getc(); ch < '0' || ch > '9'; ch = Getc())
        opt = ch == '-' ? -1 : 1;
      for (x = 0; ch <= '9' && ch >= '0'; ch = Getc())
        x = (x << 3) + (x << 1) + (ch ^ 48);
      x *= opt;
    }
    template <class Int>
    void Print(Int x) {
      if (!x) Putc('0');
      if (x < 0) Putc('-'), x = -x;
      while (x) st[++tp] = x % 10 + '0', x /= 10;
      while (tp) Putc(st[tp--]);
    }
    }  // namespace IO
    using IO::Flush;
    using IO::Input;
    using IO::Print;
    using IO::Putc;
    
    const int _ = 1e5 + 10;
    const int __ = 5e6 + 10;
    const int INF = 0x3f3f3f3f;
    
    int tot, head[_], to[_ << 1], nxt[_ << 1];
    
    void adde(int x, int y) {
      to[++tot] = y;
      nxt[tot] = head[x];
      head[x] = tot;
    }
    
    int N, M, val[_];
    
    namespace lca {
    int fa[_], son[_], dep[_], siz[_], top[_];
    void dfs1(int x, int f) {
      siz[x] = 1;
      int maxx = 0;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (y == f) continue;
        fa[y] = x, dep[y] = dep[x] + 1;
        dfs1(y, x);
        siz[x] += siz[y];
        if (siz[y] > maxx) maxx = siz[y], son[x] = y;
      }
    }
    void dfs2(int x, int topf) {
      top[x] = topf;
      if (!son[x]) return;
      dfs2(son[x], topf);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
    }
    void init() {
      dfs1(1, 0);
      dfs2(1, 1);
    }
    int query(int x, int y) {
      while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
      }
      return dep[x] < dep[y] ? x : y;
    }
    int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[query(x, y)]; }
    }  // namespace lca
    
    struct SegmentTree {
      int cnt, rt[_], ls[__], rs[__], sum[__];
      void modify(int &p, int l, int r, int x, int v) {
        if (!p) p = ++cnt;
        if (l == r) {
          sum[p] += v;
          return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid)
          modify(ls[p], l, mid, x, v);
        else
          modify(rs[p], mid + 1, r, x, v);
        sum[p] = sum[ls[p]] + sum[rs[p]];
      }
      int query(int &p, int l, int r, int x, int y) {
        if (!p) return 0;
        if (x <= l && r <= y) return sum[p];
        int mid = (l + r) >> 1;
        if (y <= mid)
          return query(ls[p], l, mid, x, y);
        else if (x > mid)
          return query(rs[p], mid + 1, r, x, y);
        else
          return query(ls[p], l, mid, x, mid) +
                 query(rs[p], mid + 1, r, mid + 1, y);
      }
    } dist, sub;
    
    int mxsiz, totsiz, root;
    int vis[_], siz[_], fa[_], dep[_];  //, d[_][20];
    
    void getroot(int x, int f) {
      siz[x] = 1;
      int maxx = 0;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        getroot(y, x);
        siz[x] += siz[y];
        maxx = max(maxx, siz[y]);
      }
      maxx = max(maxx, totsiz - siz[x]);
      if (maxx < mxsiz) mxsiz = maxx, root = x;
    }
    
    // 将距离信息插入到线段树dist中
    void dfsdist(int x, int f, int d, const int s) {
      dist.modify(dist.rt[s], 0, N, d, val[x]);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        dfsdist(y, x, d + 1, s);
      }
    }
    
    // 将距离信息插入到线段树sub中
    void dfssub(int x, int f, int d, const int s) {
      // printf("%d %d %d %d
    ", x, d, val[x], s);
      sub.modify(sub.rt[s], 0, N, d, val[x]);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        dfssub(y, x, d + 1, s);
      }
    }
    
    // 建立点分树
    void divide(int x) {
      vis[x] = 1;
      dfsdist(x, 0, 0, x);
      int nowsiz = totsiz;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y]) continue;
        mxsiz = INF, root = 0;
        totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
        getroot(y, 0);
        // printf("%d %d
    ", y, root);
        dfssub(y, 0, 1, root);
        fa[root] = x, dep[root] = dep[x] + 1;
        divide(root);
      }
    }
    
    bool yyyyy;
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("earthquake.in", "r", stdin);
      freopen("std.out", "w", stdout);
    #endif
      Input(N), Input(M);
      for (int i = 1; i <= N; ++i) Input(val[i]);
      for (int i = 1; i < N; ++i) {
        int x, y;
        Input(x), Input(y);
        adde(x, y);
        adde(y, x);
      }
      lca::init();
    
      mxsiz = INF, totsiz = N, root = 0;
      getroot(1, 0);
      divide(root);
      // for (int i = 1; i <= N; i++)
      //   for (int j = i; j; j = fa[j]) d[i][dep[i] - dep[j]] = lca::dis(i, j);
    
      int last = 0;
      while (M--) {
        int op, x, y;
        Input(op), Input(x), Input(y);
        x ^= last, y ^= last;
        cout << x << " " << y << endl;
        if (op == 0) {
          last = dist.query(dist.rt[x], 0, N, 0, y);
          for (int i = x; fa[i]; i = fa[i]) {
            int len = lca::dis(x, fa[i]);
            // int len = d[x][dep[x] - dep[fa[i]]];
            assert(y - len >= 0);
            last += dist.query(dist.rt[fa[i]], 0, N, 0, y - len);
            last -= sub.query(sub.rt[i], 0, N, 0, y - len);
          }
          cout << last << endl;
          // Print(last), Putc('
    ');
        } else if (op == 1) {
          dist.modify(dist.rt[x], 0, N, 0, y - val[x]);
          for (int i = x; fa[i]; i = fa[i]) {
            int len = lca::dis(x, fa[i]);
            // int len = d[x][dep[x] - dep[fa[i]]];
            dist.modify(dist.rt[fa[i]], 0, N, len, y - val[x]);
            sub.modify(sub.rt[i], 0, N, len, y - val[x]);
          }
          val[x] = y;
        }
      }
      // cerr << (&yyyyy - &xxxxx) / 1048576.0 << "MB" << endl;
      return Flush(), 0;
    }
    

    树状数组(7784 ms)

    #include <bits/stdc++.h>
    using namespace std;
    
    namespace IO {
    const int maxn((1 << 21) + 1);
    char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, ch,
                                           st[55];
    int opt, tp;
    char Getc() {
      return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin),
                          (iS == iT ? EOF : *iS++))
                       : *iS++);
    }
    void Flush() {
      fwrite(obuf, 1, oS - obuf, stdout);
      oS = obuf;
    }
    void Putc(char x) {
      *oS++ = x;
      if (oS == oT) Flush();
    }
    template <class Int>
    void Input(Int &x) {
      for (opt = 1, ch = Getc(); ch < '0' || ch > '9'; ch = Getc())
        opt = ch == '-' ? -1 : 1;
      for (x = 0; ch <= '9' && ch >= '0'; ch = Getc())
        x = (x << 3) + (x << 1) + (ch ^ 48);
      x *= opt;
    }
    template <class Int>
    void Print(Int x) {
      if (!x) Putc('0');
      if (x < 0) Putc('-'), x = -x;
      while (x) st[++tp] = x % 10 + '0', x /= 10;
      while (tp) Putc(st[tp--]);
    }
    }  // namespace IO
    using IO::Flush;
    using IO::Input;
    using IO::Print;
    using IO::Putc;
    
    const int _ = 1e5 + 10;
    const int __ = 5e6 + 10;
    const int INF = 0x3f3f3f3f;
    
    int tot, head[_], to[_ << 1], nxt[_ << 1];
    
    void adde(int x, int y) {
      to[++tot] = y;
      nxt[tot] = head[x];
      head[x] = tot;
    }
    
    int N, M, val[_];
    
    namespace lca {
    int fa[_], son[_], dep[_], siz[_], top[_];
    void dfs1(int x, int f) {
      siz[x] = 1;
      int maxx = 0;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (y == f) continue;
        fa[y] = x, dep[y] = dep[x] + 1;
        dfs1(y, x);
        siz[x] += siz[y];
        if (siz[y] > maxx) maxx = siz[y], son[x] = y;
      }
    }
    void dfs2(int x, int topf) {
      top[x] = topf;
      if (!son[x]) return;
      dfs2(son[x], topf);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
    }
    void init() {
      dfs1(1, 0);
      dfs2(1, 1);
    }
    int query(int x, int y) {
      while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
      }
      return dep[x] < dep[y] ? x : y;
    }
    int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[query(x, y)]; }
    }  // namespace lca
    
    struct BIT {
    #define lowbit(x) (x & -x)
      vector<int> c;
      int lim;
      BIT() {
        lim = 0;
        c.clear();
        c.push_back(0);
      }
      void insert(int x, int y) {
        ++x;
        for (; x <= lim; x += lowbit(x)) c[x] += y;
      }
      int query(int x) {
        ++x;
        x = min(x, lim);
        int ret = 0;
        for (; x; x -= lowbit(x)) ret += c[x];
        return ret;
      }
    #undef lowbit
    } dist[_], sub[_];
    
    int mxsiz, totsiz, root;
    int vis[_], siz[_], fa[_], dep[_];
    
    void getroot(int x, int f) {
      siz[x] = 1;
      int maxx = 0;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        getroot(y, x);
        siz[x] += siz[y];
        maxx = max(maxx, siz[y]);
      }
      maxx = max(maxx, totsiz - siz[x]);
      if (maxx < mxsiz) mxsiz = maxx, root = x;
    }
    
    void dfsdist(int x, int f, int d, const int s) {
      dist[s].insert(d, val[x]);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        dfsdist(y, x, d + 1, s);
      }
    }
    
    void dfssub(int x, int f, int d, const int s) {
      sub[s].insert(d, val[x]);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        dfssub(y, x, d + 1, s);
      }
    }
    
    int mxdep;
    void dfsmxdep(int x, int f, int d) {
      mxdep = max(mxdep, d);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        dfsmxdep(y, x, d + 1);
      }
    }
    
    void divide(int x) {
      vis[x] = 1;
      mxdep = 0, dfsmxdep(x, 0, 0);
      dist[x].lim = mxdep + 1;
      dist[x].c.resize(mxdep + 2, 0);
      dfsdist(x, 0, 0, x);
      int nowsiz = totsiz;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y]) continue;
        mxsiz = INF, root = 0;
        totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
        getroot(y, 0);
        mxdep = 0, dfsmxdep(y, 0, 1);
        sub[root].lim = mxdep + 1;
        sub[root].c.resize(mxdep + 2, 0);
        dfssub(y, 0, 1, root);
        fa[root] = x, dep[root] = dep[x] + 1;
        divide(root);
      }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("earthquake.in", "r", stdin);
      freopen("earthquake.out", "w", stdout);
    #endif
      Input(N), Input(M);
      for (int i = 1; i <= N; ++i) Input(val[i]);
      for (int i = 1; i < N; ++i) {
        int x, y;
        Input(x), Input(y);
        adde(x, y);
        adde(y, x);
      }
      lca::init();
    
      mxsiz = INF, totsiz = N, root = 0;
      getroot(1, 0);
      divide(root);
    
      int last = 0;
      while (M--) {
        int op, x, y;
        Input(op), Input(x), Input(y);
        x ^= last, y ^= last;
        if (op == 0) {
          last = dist[x].query(y);
          for (int i = x; fa[i]; i = fa[i]) {
            int len = lca::dis(x, fa[i]);
            if (y - len < 0) continue;
            last += dist[fa[i]].query(y - len);
            last -= sub[i].query(y - len);
          }
          Print(last), Putc('
    ');
        } else if (op == 1) {
          dist[x].insert(0, y - val[x]);
          for (int i = x; fa[i]; i = fa[i]) {
            int len = lca::dis(x, fa[i]);
            dist[fa[i]].insert(len, y - val[x]);
            sub[i].insert(len, y - val[x]);
          }
          val[x] = y;
        }
      }
      return Flush(), 0;
    }
    

    [ZJOI2015]幻想乡战略游戏

    这个题有单独的题解 链接

    BZOJ4372 烁烁的游戏

    这题仿照震波的做法:

    还是动态点分治的套路,对于每个点开两棵动态开点的线段树或是树状数组,以距离为下标,分别存储到点(x)距离为(y)的增量,以及到点(fa[x])距离为(y)的增量(用于容斥)。

    查询和修改不断在点分树上暴跳父亲即可。

    最后,由于上次震波的惨痛教训,我决定写树状数组:

    2020-01-01 18-39-45屏幕截图.png

    树状数组跑的飞快,开心!

    #include <bits/stdc++.h>
    using namespace std;
    
    #ifndef ONLINE_JUDGE
    bool xxx;
    #endif
    
    inline int ty() {
      char ch = getchar();
      int x = 0, f = 1;
      while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
      }
      while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
      }
      return x * f;
    }
    
    const int _ = 1e5 + 10;
    const int __ = 1e7 + 10;
    const int INF = 0x3f3f3f3f;
    int N, M, tot, head[_], to[_ << 1], nxt[_ << 1];
    
    void adde(int x, int y) {
      to[++tot] = y;
      nxt[tot] = head[x];
      head[x] = tot;
    }
    
    namespace lca {
    int fa[_], son[_], dep[_], siz[_], top[_];
    void dfs1(int x, int f) {
      siz[x] = 1;
      int maxx = 0;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (y == f) continue;
        fa[y] = x, dep[y] = dep[x] + 1;
        dfs1(y, x);
        siz[x] += siz[y];
        if (siz[y] > maxx) maxx = siz[y], son[x] = y;
      }
    }
    void dfs2(int x, int topf) {
      top[x] = topf;
      if (!son[x]) return;
      dfs2(son[x], topf);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
    }
    void init() {
      dep[1] = 1, dfs1(1, 0);
      dfs2(1, 1);
    }
    int query(int x, int y) {
      while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
      }
      return dep[x] < dep[y] ? x : y;
    }
    int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[query(x, y)]; }
    }  // namespace lca
    
    struct BIT {
    #define lowbit(x) (x & -x)
      vector<int> tr;
      int lim;
      void insert(int x, int y) {
        ++x;
        // assert(x<=lim);
        x = min(x, lim);
        for (; x <= lim; x += lowbit(x)) tr[x] += y;
      }
      int query(int x) {
        ++x;
        x = min(x, lim);
        int ret = 0;
        for (; x; x -= lowbit(x)) ret += tr[x];
        return ret;
      }
    #undef lowbit
    } dist[_], sub[_];
    
    int mxsiz, totsiz, root, vis[_], fa[_], siz[_];
    
    void getroot(int x, int f) {
      siz[x] = 1;
      int maxx = 0;
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        getroot(y, x);
        siz[x] += siz[y];
        maxx = max(maxx, siz[y]);
      }
      maxx = max(maxx, totsiz - siz[x]);
      if (maxx < mxsiz) mxsiz = maxx, root = x;
    }
    
    int mxdep;
    
    void dfs(int x, int f, int d) {
      mxdep = max(mxdep, d);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        dfs(y, x, d + 1);
      }
    }
    
    void divide(int x) {
      vis[x] = 1;
      int nowsiz = totsiz;
      mxdep = 0, dfs(x, 0, 0);
      dist[x].lim = mxdep + 1;
      // printf("%d %d %d
    ", x, mxdep, dist[x].lim);
      dist[x].tr.resize(mxdep + 3);
      for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y]) continue;
        mxsiz = INF, root = 0;
        totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
        getroot(y, 0);
        mxdep = 0, dfs(y, 0, 1);
        sub[root].lim = mxdep + 1;
        sub[root].tr.resize(mxdep + 2, 0);
        fa[root] = x;
        divide(root);
      }
    }
    
    int query(int x) {
      int ret = dist[x].query(dist[x].lim);
      for (int i = x; fa[i]; i = fa[i]) {
        int len = lca::dis(x, fa[i]);
        ret += dist[fa[i]].query(dist[fa[i]].lim) - dist[fa[i]].query(len - 1);
        ret -= sub[i].query(sub[i].lim) - sub[i].query(len - 1);
      }
      return ret;
    }
    
    void modify(int x, int d, int v) {
      dist[x].insert(d, v);
      // assert(d <= dist[x].lim);
      for (int i = x; fa[i]; i = fa[i]) {
        int len = lca::dis(x, fa[i]);
        if (d < len) continue;
        dist[fa[i]].insert(d - len, v);
        // assert(d - len <= dist[fa[i]].lim);
        sub[i].insert(d - len, v);
        // assert(d - len <= sub[i].lim);
      }
    }
    
    #ifndef ONLINE_JUDGE
    bool yyy;
    #endif
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("game.in", "r", stdin);
      freopen("game.out", "w", stdout);
    #endif
      N = ty(), M = ty();
      for (int i = 1; i < N; ++i) {
        int x = ty(), y = ty();
        adde(x, y);
        adde(y, x);
      }
      lca::init();
    
      mxsiz = INF, totsiz = N, root = 0;
      getroot(1, 0);
      divide(1);
    
      while (M--) {
        char op[5];
        int x, y, z;
        scanf("%s", op), x = ty();
        if (op[0] == 'Q')
          printf("%d
    ", query(x));
        else if (op[0] == 'M') {
          y = ty(), z = ty();
          modify(x, y, z);
        }
      }
    
    #ifndef ONLINE_JUDGE
      cerr << (&yyy - &xxx) / 1048576.0 << "MB" << endl;
    #endif
      return 0;
    }
    

    [WC2014]紫荆花之恋

    事实上,我学动态点分治就是为了写这道题,链接

    既然选择了远方,便只顾风雨兼程。
  • 相关阅读:
    toString()和toLocaleString()有什么区别
    解决浏览器跨域问题
    JavaScript排序,不只是冒泡
    input disabled的情况下在IOS设备中怎么修改颜色
    jquery api 中文文档
    javascript中var that=this的意思
    JS中(function(){xxx})(); 这种写法的意思
    http-equiv 的使用
    $(function(){......})的意思
    js中(function(){…})()立即执行函数写法理解
  • 原文地址:https://www.cnblogs.com/newbielyx/p/12124269.html
Copyright © 2011-2022 走看看