[WC 2014]紫荆花之恋

Posted NaVi_Awson

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[WC 2014]紫荆花之恋相关的知识,希望对你有一定的参考价值。

Description

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

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

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

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

Input

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

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

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

接下来 $n$ 行中第 $i$ 行有三个非负整数 $a_i, c_i, r_i$,表示结点 $i$ 的父节点的编号为 $a_i xor (\\text{last_ans} \\bmod 10^9)$(其中 $xor$ 表示异或,$\\bmod$表示取余,数据保证这样操作后得到的结果介于 $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 \\times 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\\texttt{s}$

空间限制:$512\\texttt{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(\'\\n\');
175     }
176 }
177 int main() {
178     srand(time(0)); work();
179     return 0;
180 }

 

以上是关于[WC 2014]紫荆花之恋的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ3435[Wc2014]紫荆花之恋 替罪点分树+SBT

Wc2014 紫荆花之恋

#55. WC2014紫荆花之恋

[WC2014]紫荆花之恋

luogu P3920 [WC2014]紫荆花之恋

UOJ#55 [WC2014]紫荆花之恋