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