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 }