• [WC 2014]紫荆花之恋


    Description

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

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

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

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

    Input

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

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

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

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

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

    Output

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

    Sample Input

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

    Sample Output

    0
    1
    2
    4
    7
    

    Hint

    对于所有数据,满足 $1 leq c_i leq 10000$,$a_i leq 2 imes 10^9$,$r_i leq 10^9$。

    测试点编号约定
    1, 2 $n leq 100$
    3, 4 $n leq 1000$
    5, 6, 7, 8 $n leq 100000$,节点 $1$ 最多有两个子节点,其它节点最多有一个子节点
    9, 10 $n leq 100000$,$r_i leq 10$
    11, 12 $n leq 100000$,这棵树是随机生成的
    13, 14, 15 $n leq 70000$
    16, 17, 18, 19, 20 $n leq 100000$

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

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

    时间限制:$12 exttt{s}$

    空间限制:$512 exttt{MB}$

    题解

    有生之年竟然能切这道题...尽管常数大得吓人...但在 $UOJ$ 上 A 的掉。

    用动态点分治做过[ZJOI 2007]Hide 捉迷藏[ZJOI 2015]幻想乡战略游戏(或[HNOI 2015]开店)应该来说不是很难的...

    做法还是自己 YY 的,不知道是否有更好的方法。

    首先,对于题目要求 $dist(u, v) leq r_u+r_v$ ,我们从 $u$ 在点分树向上跳的时候,第一次处理到含点 $v$ 的重心,那么这个重心就是 $lca(u, v)$ ,我们对这个 $lca$ 进行处理。

    由于满足上式,所以 $dist(u, lca)+dist(v, lca) leq r_u+r_v$ 等价于若点 $v$ 满足 $dist(v, lca)-r_v leq r_u-dist(u, lca)$ 那么显然 $v$ 是满足与 $u$ 是好朋♂友的。

    那么我们可以在每个节点上建一棵平衡树,维护以其为重心的子树中 $dist(v, lca)-r_v$ 的值,显然统计答案就是平衡树中 $leq r_u-dist(u, lca)$ 的个数。

    按照套路,为了防止重复计算,我们需要减去会在这个重心的父亲处重复计算的值。记在点分树中 $u$ 节点的父亲为 $fa_u$ ,所以我们再开一个平衡树来存 $dist(fa_{lca}, v)-r_v$ ,额外减去的就是第二棵平衡树中 $leq r_u-dist(fa_{lca}, u)$ 的个数。

    对于加点的操作,我们直接在原图上添加,用替罪羊的思想,若以 $u$ 为根的子树大小出现不平衡,直接将以 $u$ 为根的整棵子树拍平用点分治重建。

      1 //It is made by Awson on 2018.1.10
      2 #include <set>
      3 #include <map>
      4 #include <cmath>
      5 #include <ctime>
      6 #include <queue>
      7 #include <stack>
      8 #include <cstdio>
      9 #include <string>
     10 #include <vector>
     11 #include <cstdlib>
     12 #include <cstring>
     13 #include <iostream>
     14 #include <algorithm>
     15 #define LL long long
     16 #define lowbit(x) ((x)&(-(x)))
     17 #define Max(a, b) ((a) > (b) ? (a) : (b))
     18 #define Min(a, b) ((a) < (b) ? (a) : (b))
     19 #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
     20 using namespace std;
     21 const int N = 100000;
     22 const int MAXS = N*100;
     23 const double alpha = 0.88;
     24 const int MOD = 1e9;
     25 const int INF = ~0u>>1;
     26 void read(int &x) {
     27     char ch; bool flag = 0;
     28     for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
     29     for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
     30     x *= 1-2*flag;
     31 }
     32 void write(LL x) {
     33     if (x > 9) write(x/10);
     34     putchar(x%10+48);
     35 }
     36 
     37 int n, lim, a, c, r[N+5], fa[N+5], vis[N+5], size[N+5]; LL last_ans;
     38 struct tt {
     39     int to, next, cost;
     40 }edge[(N<<1)+5];
     41 int path[N+5], top;
     42 void add(int u, int v, int c) {
     43     edge[++top].to = v, edge[top].cost = c, edge[top].next = path[u]; path[u] = top;
     44 }
     45 vector<int>to[N+5];
     46 struct Treap {
     47     int root[N+5], ch[MAXS+5][2], key[MAXS+5], lev[MAXS+5], size[MAXS+5], pos;
     48     queue<int>mem;
     49     void newnode(int &o, int keyy) {
     50     if (!mem.empty()) o = mem.front(), mem.pop();
     51     else o = ++pos;
     52     ch[o][0] = ch[o][1] = 0, lev[o] = rand(), key[o] = keyy, size[o] = 1;
     53     }
     54     void pushup(int o) {size[o] = size[ch[o][0]]+size[ch[o][1]]+1; }
     55     void rotate(int &o, int kind) {
     56     int x = ch[o][!kind];
     57     ch[o][!kind] = ch[x][kind];
     58     ch[x][kind] = o;
     59     o = x;
     60     }
     61     void insert(int &o, int keyy) {
     62     if (!o) {newnode(o, keyy); return; }
     63     size[o]++;
     64     int kind = keyy >= key[o];
     65     insert(ch[o][kind], keyy);
     66     if (lev[ch[o][kind]] < lev[o]) rotate(o, !kind), pushup(ch[o][!kind]), pushup(o);
     67     }
     68     int query(int o, int keyy) {
     69     if (!o) return 0;
     70     if (keyy < key[o]) return query(ch[o][0], keyy);
     71     else return size[ch[o][0]]+1+query(ch[o][1], keyy);
     72     }
     73     void travel(int o) {
     74     if (ch[o][0]) travel(ch[o][0]);
     75     mem.push(o);
     76     if (ch[o][1]) travel(ch[o][1]);
     77     }
     78     void recycle(int id) {
     79     if (!root[id]) return;
     80     travel(root[id]); root[id] = 0;
     81     }
     82 }T1, T2;
     83 namespace LCA {
     84     int dep[N+5], f[N+5][25], dis[N+5];
     85     void update(int fa, int o, int dist) {
     86     f[o][0] = fa, dep[o] = dep[fa]+1, dis[o] = dis[fa]+dist;
     87     for (int t = 1; t <= lim; t++) f[o][t] = f[f[o][t-1]][t-1];
     88     }
     89     int query(int x, int y) {
     90     if (dep[x] < dep[y]) Swap(x, y);
     91     for (int i = lim; i >= 0; i--) if (dep[f[x][i]] >= dep[y]) x = f[x][i];
     92     if (x == y) return x;
     93     for (int i = lim; i >= 0; i--) if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
     94     return f[x][0];
     95     }
     96     int dist(int x, int y) {return dis[x]+dis[y]-(dis[query(x, y)]<<1); }
     97 }
     98 namespace Point_divide {
     99     int size[N+5], mx[N+5], root, minsize;
    100     void get_size(int o, int fa) {
    101     size[o] = 1, mx[o] = 0;
    102     for (int i = path[o]; i; i = edge[i].next)
    103         if (edge[i].to != fa && !vis[edge[i].to]) {
    104         get_size(edge[i].to, o);
    105         size[o] += size[edge[i].to];
    106         if (size[edge[i].to] > mx[o]) mx[o] = size[edge[i].to];
    107         }
    108     }
    109     void get_root(int o, int pa, int fa) {
    110     mx[o] = Max(mx[o], size[pa]-size[o]);
    111     if (mx[o] < minsize) minsize = mx[root = o];
    112     for (int i = path[o]; i; i = edge[i].next)
    113         if (edge[i].to != fa && !vis[edge[i].to]) get_root(edge[i].to, pa, o);
    114     }
    115     void get_push(int o, int pa, int da, int fa, int cost) {
    116     T1.insert(T1.root[da], cost-r[o]); if (pa) T2.insert(T2.root[da], LCA::dist(pa, o)-r[o]);
    117     for (int i = path[o]; i; i = edge[i].next)
    118         if (edge[i].to != fa && !vis[edge[i].to]) get_push(edge[i].to, pa, da, o, cost+edge[i].cost);
    119     }
    120     int work(int o, int pa) {
    121     minsize = INF, get_size(o, 0), get_root(o, o, 0); vis[root] = 1, fa[root] = pa; if (pa) to[pa].push_back(root); int rt = root;
    122     T1.insert(T1.root[root], -r[root]); if (pa) T2.insert(T2.root[root], LCA::dist(pa, root)-r[root]);
    123     for (int i = path[root]; i; i = edge[i].next) 
    124         if (!vis[edge[i].to]) get_push(edge[i].to, pa, root, 0, edge[i].cost);
    125     for (int i = path[root]; i; i = edge[i].next)
    126         if (!vis[edge[i].to]) work(edge[i].to, rt);
    127     return rt;
    128     }
    129 }
    130 int balance(int o, int son) {return size[o]*alpha >= size[son]; }
    131 
    132 int update(int o) {
    133     int w = 0; size[o]++;
    134     for (int x = o; x; x = fa[x]) {
    135     if (fa[x]) size[fa[x]]++;
    136     T1.insert(T1.root[x], LCA::dist(x, o)-r[o]);
    137     if (fa[x]) T2.insert(T2.root[x], LCA::dist(fa[x], o)-r[o]);
    138     if (fa[x] && !balance(fa[x], x)) w = fa[x];
    139     }
    140     return w;
    141 }
    142 void destroy(int o) {
    143     vis[o] = 0;
    144     for (int i = 0, tol = to[o].size(); i < tol; i++) destroy(to[o][i]);
    145     T1.recycle(o), T2.recycle(o); to[o].clear();
    146 }
    147 void pushup(int o) {
    148     size[o] = 1;
    149     for (int i = 0, tol = to[o].size(); i < tol; i++) pushup(to[o][i]), size[o] += size[to[o][i]];
    150 }
    151 void rebuild(int o) {
    152     int x = -1, f = fa[o];
    153     if (f) for (int i = 0, tol = to[fa[o]].size(); i < tol; i++) if (to[fa[o]][i] == o) {x = i; break; }
    154     destroy(o);
    155     int y = Point_divide::work(o, fa[o]);
    156     if (f) {to[f].pop_back(); to[f][x] = y; }
    157     pushup(y);
    158 }
    159 int query(int o) {
    160     int ans = 0;
    161     for (int x = o; x; x = fa[x]) {
    162     ans += T1.query(T1.root[x], r[o]-LCA::dist(o, x));
    163     if (fa[x]) ans -= T2.query(T2.root[x], r[o]-LCA::dist(o, fa[x]));
    164     }
    165     return ans-1;
    166 }
    167 void work() {
    168     scanf("%d", &n), scanf("%d" ,&n); lim = log(n)/log(2);
    169     for (int i = 1; i <= n; i++) {
    170     scanf("%d%d%d", &a, &c, &r[i]);
    171     a = a^(last_ans%MOD);
    172     if (a != 0) add(a, i, c), add(i, a, c); fa[i] = a, to[a].push_back(i); vis[i] = 1;
    173     LCA::update(a, i, c); int x = update(i); if (x) rebuild(x);
    174     last_ans += query(i); write(last_ans); putchar('
    ');
    175     }
    176 }
    177 int main() {
    178     srand(time(0)); work();
    179     return 0;
    180 }
  • 相关阅读:
    STL的二分查找binary_search
    转_HDU_1907&2509 博弈(Nim博弈变形)
    HDU3589_Jacobi symbol_二次剩余
    转载_模运算_归纳得很精华啊
    HDU3501_calculation2_欧拉函数求和
    【转】求小于等于N的与N互质的数的和
    HDU3328_Flipper_纸牌翻转_模拟题
    转_求逆元的两种方法
    HDU2541_Simple Addition Expression
    MySql数据同步(双机热备)已正式应用上平台
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/8253119.html
Copyright © 2020-2023  润新知