zoukankan      html  css  js  c++  java
  • UOJ#55 [WC2014]紫荆花之恋

    题目描述

    强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。

    仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点,每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 (i) 都有一个感受能力值 (r_i),小精灵 (i, j) 成为朋友当且仅当在树上 (i)(j) 的距离 ( ext{dist}(i, j) leq r_i + r_j),其中 ( ext{dist}(i, j)) 表示在这个树上从 (i)(j) 的唯一路径上所有边的边权和。

    强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。

    我们假定这个树一开始为空,节点按照加入的顺序从 (1) 开始编号。由于强强非常好奇,你必须在他每次出现新结点后马上给出总共的朋友对数,不能拖延哦。

    输入格式

    第一行包含一个整数,表示测试点编号。

    第二行包含一个正整数 (n),表示总共要加入的节点数。

    我们令加入节点前的总共朋友对数是 ( ext{last_ans}),在一开始时它的值为 (0)

    接下来 (n) 行中第 (i) 行有三个非负整数 (a_i, c_i, r_i),表示结点 (i) 的父节点的编号为 (a_i oplus ( ext{last_ans} mod 10^9))(其中 (oplus) 表示异或,(mod)表示取余,数据保证这样操作后得到的结果介于 (1)(i - 1) 之间),与父结点之间的边权为 (c_i),节点 (i) 上小精灵的感受能力值为 (r_i)

    注意 (a_1 = c_1 = 0),表示 (1) 号节点是根结点,对于 (i > 1),父节点的编号至少为 (1)

    输出格式

    包含 (n) 行,每行输出 (1) 个整数,表示加入第 (i) 个点之后,树上有几对朋友。

    样例一

    input

        0
        5
        0 0 6
        1 2 4
        0 9 4
        0 5 5
        0 2 4
    

    output

        0
        1
        2
        4
        7
    

    样例二

    见样例数据下载。

    限制与约定

    对于所有数据,满足 (1 leq c_i leq 10000)(a_i leq 2 imes 10^9)(r_i leq 10^9)(n leq 100000)

    此题 hack 时忽略输入数据中给定的测试点编号对测试点的限制。

    祝大家一遍 AC,求不虐萌萌哒测评机!

    时间限制(12 exttt{s})

    空间限制(512 exttt{MB})

    题解

    首先,我们发现实际上每次只需要统计新加进的结点有多少合法解。那么,我们将(dist(i, j))写成(dist(i,p)+dist(j,p)),其中(p)(i)(j)的LCA。

    那么,

    [egin{aligned}dist(i,j)&leq r_i + r_j\ Leftrightarrow dist(i,p)+dist(j,p)&leq r_i+r_j\ Leftrightarrow r_i-dist(i,p)&geq dist(j,p)-r_jend{aligned}]

    所以,我们枚举新加的结点的所有祖先(p),计算以(p)为LCA的满足条件的点对((i,j))有多少个即可。

    计算时,先查询(p)子树在添加(i)之前有多少(j)满足(r_i-dist(i,p)geq dist(j,p)-r_j),再减去其中LCA不是(p)的(即,(i)(j)(p)的同一棵子树里,这样我们就要查询在这棵子树里有多少(j)满足此条件)。

    然而,要高效地维护这个信息,就需要在每个结点上维护一个Treap(记录所有的(dist(j,p)-r_j)),但这样当树退化为链,单次加入时间复杂度增加为(O(nlogn)),空间复杂度增加为(O(n^2))

    于是,借鉴替罪羊树的思想,我们在某个结点子树内的点个数大于其父亲子树内点的个数的(alphain(0,1))倍的时候暴力重构其父亲的子树。既然要求重构之后尽量平衡,理所当然地选用点分治。

    这样,上面的分析中所有的“父亲”和“祖先”都应理解为点分治树上的祖先(LCA当然也是),但是答案仍是正确的,因为我们仍然不重不漏地枚举了所有可能的点。

    代码实现上,还要写一个倍增LCA以查询(dist),因为点分治之后的祖先不一定是原树上的祖先,不能直接用(dep)相减。

    哦,还有,要写垃圾回收,因为有重构treap,之前的内存必须要重复利用。

    代码

    #include <algorithm>
    #include <cstdio>
    #include <cstdlib>
    #include <stack>
    #include <set>
    typedef long long LL;
    const int N = 100050;
    int pre[N], to[N * 2], nxt[N * 2], c[N * 2], cnt = 0;
    int r[N];
    inline void add_edge(int x, int y, int v) {
      nxt[cnt] = pre[x];
      c[cnt] = v;
      to[pre[x] = cnt++] = y;
      nxt[cnt] = pre[y];
      c[cnt] = v;
      to[pre[y] = cnt++] = x;
    }
    namespace Tree{
      int dep[N], dep2[N], fa[N][17];
      void insert(int x, int c, int f) {
        add_edge(x, f, c);
        dep[x] = dep[fa[x][0] = f] + 1;
        dep2[x] = dep2[f] + c;
        for (int i = 1; i < 17; ++i)
          fa[x][i] = fa[fa[x][i - 1]][i - 1];
      }
      int LCA(int x, int y) {
        if (dep[x] > dep[y]) std::swap(x, y);
        for (int i = 16; ~i; --i)
          if (dep[fa[y][i]] >= dep[x])
            y = fa[y][i];
        for (int i = 16; ~i; --i)
          if (fa[x][i] != fa[y][i]) {
            x = fa[x][i];
            y = fa[y][i];
          }
        return x == y ? x : fa[x][0];
      }
      int dis(int x, int y) {
        int l = LCA(x, y);
        return dep2[x] + dep2[y] - 2 * dep2[l];
      }
    };
    struct Treap;
    typedef Treap* PTreap;
    struct Treap{
      static std::stack<PTreap> bin;
      PTreap lch, rch;
      int val, key, cnt, siz;
      void* operator new(size_t, int v) {
        Treap *res;
        res = bin.top();
        bin.pop();
        res->val = v; res->key = rand();
        res->cnt = 1; res->siz = 1;
        res->lch = res->rch = NULL;
        return res;
      }
      void operator delete(void *t) {
        bin.push((PTreap)t);
      }
      void update() {
        siz = cnt;
        if (lch != NULL) siz += lch->siz;
        if (rch != NULL) siz += rch->siz;
      }
      friend void Zig(PTreap &t) { //右旋
        PTreap l = t->lch;
        t->lch = l->rch;
        l->rch = t;
        t->update();
        l->update();
        t = l;
      }
      friend void Zag(PTreap &t) { //左旋
        Treap *r = t->rch;
        t->rch = r->lch;
        r->lch = t;
        t->update();
        r->update();
        t = r;
      }
      friend int query(PTreap o, int x) {
        if (o == NULL) return 0;
        if (o->val > x) return query(o->lch, x);
        else return query(o->rch, x) + (o->lch == NULL ? 0 : o->lch->siz) + o->cnt;
      }
      friend void insert(PTreap &o, int x) {
        if (o == NULL)
          o = new (x)Treap;
        else if (o->val == x)
          ++o->cnt;
        else if (o->val > x) {
          insert(o->lch, x);
          if (o->lch->key > o->key)
            Zig(o);
        } else {
          insert(o->rch, x);
          if (o->rch->key > o->key)
            Zag(o);
        }
        o->update();
      }
      friend void remove(PTreap &x) {
        if (x == NULL) return;
        remove(x->lch);
        remove(x->rch);
        delete x; x = NULL;
      }
    };
    std::stack<PTreap> Treap::bin;
    namespace Dynamic_TreeDivision{
      PTreap tree[N], sonTree[N];
      int time, vise[N * 2];
      int fa[N], vis[N];
      std::set<int> son[N];
      void remove(int x) {
        vis[x] = time;
        for (std::set<int>::iterator i = son[x].begin(); i != son[x].end(); ++i) {
          remove(*i);
          remove(sonTree[*i]);
        }
        son[x].clear();
        remove(tree[x]);
      }
      int getCentre(int x, int f, int siz, int &ct) {
        int res = 1;
        bool ok = true;
        for (int i = pre[x]; ~i; i = nxt[i]) {
          if (vise[i] == time) continue;
          if (to[i] == f) continue;
          if (vis[to[i]] != time) continue;
          int ss = getCentre(to[i], x, siz, ct);
          if (ss > siz / 2) ok = false;
          res += ss;
        }
        if (siz - res > siz / 2) ok = false;
        if (ok) ct = x;
        return res;
      }
      void insertAll(int x, int f, int dep, PTreap &p) {
        insert(p, dep - r[x]);
        for (int i = pre[x]; ~i; i = nxt[i]) {
          if (vise[i] == time) continue;
          if (to[i] == f) continue;
          if (vis[to[i]] != time) continue;
          insertAll(to[i], x, dep + c[i], p);
        }
      }
      int divide(int x) {
        getCentre(x, 0, getCentre(x, 0, 1000000000, x), x);
        insertAll(x, 0, 0, tree[x]);
        for (int i = pre[x]; ~i; i = nxt[i]) {
          if (vise[i] == time) continue;
          if (vis[to[i]] != time) continue;
          vise[i] = vise[i ^ 1] = time;
          PTreap p = NULL;
          insertAll(to[i], 0, c[i], p);
          int s = divide(to[i]);
          fa[s] = x;
          son[x].insert(s);
          sonTree[s] = p;
        }
        return x;
      }
      void rebuild(int x) {
        ++time;
        remove(x);
        int ff = fa[x];
        PTreap p = sonTree[x];
        sonTree[x] = NULL;
        if (ff != 0) son[ff].erase(x);
        x = divide(x);
        fa[x] = ff;
        sonTree[x] = p;
        if (ff != 0) son[ff].insert(x);
      }
      LL insert(int x, int f) {
        LL ans = 0;
        son[f].insert(x);
        fa[x] = f;
        for (int i = x; i; i = fa[i]) {
          if (fa[i] != 0) {
            int d = Tree::dis(fa[i], x);
            ans += query(tree[fa[i]], r[x] - d);
            ans -= query(sonTree[i], r[x] - d);
            insert(sonTree[i], d - r[x]);
          }
          int d = Tree::dis(i, x);
          insert(tree[i], d - r[x]);
        }
        int rebuildx = 0;
        for (int i = x; fa[i]; i = fa[i])
          if (tree[i]->siz > tree[fa[i]]->siz * 0.88)
            rebuildx = fa[i];
        if (rebuildx) rebuild(rebuildx);
        return ans;
      }
    };
    Treap node[N * 100];
    int main() {
      for (int i = 0; i < N * 100; ++i)
        Treap::bin.push(node + i);
      int n, a, cc, v;
      scanf("%*d%d", &n);
      LL lastans = 0;
      pre[0] = -1;
      for (int i = 1; i <= n; ++i) {
        scanf("%d%d%d", &a, &cc, &v);
        r[i] = v;
        a ^= lastans % 1000000000;
        pre[i] = -1;
        Tree::insert(i, cc, a);
        lastans += Dynamic_TreeDivision::insert(i, a);
        printf("%lld
    ", lastans);
      }
      return 0;
    }
    
    
  • 相关阅读:
    XStream和Dom4j的区别
    tomcat 性能优化(内存优化 线程优化)
    安装tomcat
    python 全栈开发,Day10(动态参数,命名空间,作用域,函数嵌套)
    python 全栈开发,Day9(函数的初始,返回值,传参,三元运算)
    python 全栈开发,Day8(文件操作)
    python 全栈开发,Day7(元组转换,列表以及字典的坑,集合,关系测试,深浅copy,编码补充)
    python 全栈开发,Day6(is,小数据池,编码转换)
    python 全栈开发,Day5(字典,增删改查,其他操作方法)
    python 全栈开发,Day4(列表切片,增删改查,常用操作方法,元组,range,join)
  • 原文地址:https://www.cnblogs.com/y-clever/p/8057789.html
Copyright © 2011-2022 走看看