1. 程式人生 > >[WC 2014]紫荊花之戀

[WC 2014]紫荊花之戀

在外 blog cost har 出現 rand 忽略 new 結點之間

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]紫荊花之戀