UOJ55 [WC2014]紫荆花之恋
Description
有一棵树,最开始是一棵空树。有$n$个操作,每次操作给出$a,c,r$,表示新的节点父亲编号为$a$,其与父亲之前的边权为$c$,其“感受能力”为$r$。
每次操作后询问满足$dist(i, j)\leqslant r_i+r_j$的$(i,j)$有多少对,其中$dist$表示两点间唯一路径上的边权和。
所有$a_i$与上次的答案$last\_ans$(初始为$0$)异或后对$10^9$取模。
Solution
首先,我们发现实际上每次只需要统计新加进的结点有多少合法解。那么,我们将$dist(i, j)$写成$dist(i,p)+dist(j,p)$,其中$p$是$i$和$j$的LCA。
那么,
$$\begin{aligned}dist(i,j)&\leqslant r_i + r_j\\ \Leftrightarrow dist(i,p)+dist(j,p)&\leqslant r_i+r_j\\ \Leftrightarrow r_i-dist(i,p)&\geq dist(j,p)-r_j\end{aligned}$$
所以,我们枚举新加的结点的所有祖先$p$,计算以$p$为LCA的满足条件的点对$(i,j)$有多少个即可。
计算时,先查询$p$子树在添加$i$之前有多少$j$满足$r_i-dist(i,p)\geq dist(j,p)-r_j$,再减去其中LCA不是$p$的(即,$i$和$j$在$p$的同一棵子树里,这样我们就要查询在这棵子树里有多少$j$满足此条件)。
然而,要高效地维护这个信息,就需要在每个结点上维护一个Treap(记录所有的$dist(j,p)-r_j$),但这样当树退化为链,单次加入时间复杂度增加为$O(nlogn)$,空间复杂度增加为$O(n^2)$。
于是,借鉴替罪羊树的思想,我们在某个结点子树内的点个数大于其父亲子树内点的个数的$\alpha\in(0,1)$倍的时候暴力重构其父亲的子树。既然要求重构之后尽量平衡,理所当然地选用点分治。
这样,上面的分析中所有的“父亲”和“祖先”都应理解为点分治树上的祖先(LCA当然也是),但是答案仍是正确的,因为我们仍然不重不漏地枚举了所有可能的点。
代码实现上,还要写一个倍增LCA以查询$dist$,因为点分治之后的祖先不一定是原树上的祖先,不能直接用$dep$相减。
哦,还有,要写垃圾回收,因为有重构treap,之前的内存必须要重复利用。
Code
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <stack>
#include <set>
typedef long long LL;
const int N = 100050;
int pre[N], to[N * 2], nxt[N * 2], c[N * 2], cnt = 0;
int r[N];
inline void add_edge(int x, int y, int v) {
[cnt] = pre[x];
nxt[cnt] = v;
c[pre[x] = cnt++] = y;
to[cnt] = pre[y];
nxt[cnt] = v;
c[pre[y] = cnt++] = x;
to}
namespace Tree{
int dep[N], dep2[N], fa[N][17];
void insert(int x, int c, int f) {
(x, f, c);
add_edge[x] = dep[fa[x][0] = f] + 1;
dep[x] = dep2[f] + c;
dep2for (int i = 1; i < 17; ++i)
[x][i] = fa[fa[x][i - 1]][i - 1];
fa}
int LCA(int x, int y) {
if (dep[x] > dep[y]) std::swap(x, y);
for (int i = 16; ~i; --i)
if (dep[fa[y][i]] >= dep[x])
= fa[y][i];
y for (int i = 16; ~i; --i)
if (fa[x][i] != fa[y][i]) {
= fa[x][i];
x = fa[y][i];
y }
return x == y ? x : fa[x][0];
}
int dis(int x, int y) {
int l = LCA(x, y);
return dep2[x] + dep2[y] - 2 * dep2[l];
}
};
struct Treap;
typedef Treap* PTreap;
struct Treap{
static std::stack<PTreap> bin;
, rch;
PTreap lchint val, key, cnt, siz;
void* operator new(size_t, int v) {
*res;
Treap = bin.top();
res .pop();
bin->val = v; res->key = rand();
res->cnt = 1; res->siz = 1;
res->lch = res->rch = NULL;
resreturn res;
}
void operator delete(void *t) {
.push((PTreap)t);
bin}
void update() {
= cnt;
siz if (lch != NULL) siz += lch->siz;
if (rch != NULL) siz += rch->siz;
}
friend void Zig(PTreap &t) { //右旋
= t->lch;
PTreap l ->lch = l->rch;
t->rch = t;
l->update();
t->update();
l= l;
t }
friend void Zag(PTreap &t) { //左旋
*r = t->rch;
Treap ->rch = r->lch;
t->lch = t;
r->update();
t->update();
r= r;
t }
friend int query(PTreap o, int x) {
if (o == NULL) return 0;
if (o->val > x) return query(o->lch, x);
else return query(o->rch, x) + (o->lch == NULL ? 0 : o->lch->siz) + o->cnt;
}
friend void insert(PTreap &o, int x) {
if (o == NULL)
= new (x)Treap;
o else if (o->val == x)
++o->cnt;
else if (o->val > x) {
(o->lch, x);
insertif (o->lch->key > o->key)
(o);
Zig} else {
(o->rch, x);
insertif (o->rch->key > o->key)
(o);
Zag}
->update();
o}
friend void remove(PTreap &x) {
if (x == NULL) return;
(x->lch);
remove(x->rch);
removedelete x; x = NULL;
}
};
std::stack<PTreap> Treap::bin;
namespace Dynamic_TreeDivision{
[N], sonTree[N];
PTreap treeint time, vise[N * 2];
int fa[N], vis[N];
std::set<int> son[N];
void remove(int x) {
[x] = time;
visfor (std::set<int>::iterator i = son[x].begin(); i != son[x].end(); ++i) {
(*i);
remove(sonTree[*i]);
remove}
[x].clear();
son(tree[x]);
remove}
int getCentre(int x, int f, int siz, int &ct) {
int res = 1;
bool ok = true;
for (int i = pre[x]; ~i; i = nxt[i]) {
if (vise[i] == time) continue;
if (to[i] == f) continue;
if (vis[to[i]] != time) continue;
int ss = getCentre(to[i], x, siz, ct);
if (ss > siz / 2) ok = false;
+= ss;
res }
if (siz - res > siz / 2) ok = false;
if (ok) ct = x;
return res;
}
void insertAll(int x, int f, int dep, PTreap &p) {
(p, dep - r[x]);
insertfor (int i = pre[x]; ~i; i = nxt[i]) {
if (vise[i] == time) continue;
if (to[i] == f) continue;
if (vis[to[i]] != time) continue;
(to[i], x, dep + c[i], p);
insertAll}
}
int divide(int x) {
(x, 0, getCentre(x, 0, 1000000000, x), x);
getCentre(x, 0, 0, tree[x]);
insertAllfor (int i = pre[x]; ~i; i = nxt[i]) {
if (vise[i] == time) continue;
if (vis[to[i]] != time) continue;
[i] = vise[i ^ 1] = time;
vise= NULL;
PTreap p (to[i], 0, c[i], p);
insertAllint s = divide(to[i]);
[s] = x;
fa[x].insert(s);
son[s] = p;
sonTree}
return x;
}
void rebuild(int x) {
++time;
(x);
removeint ff = fa[x];
= sonTree[x];
PTreap p [x] = NULL;
sonTreeif (ff != 0) son[ff].erase(x);
= divide(x);
x [x] = ff;
fa[x] = p;
sonTreeif (ff != 0) son[ff].insert(x);
}
(int x, int f) {
LL insert= 0;
LL ans [f].insert(x);
son[x] = f;
fafor (int i = x; i; i = fa[i]) {
if (fa[i] != 0) {
int d = Tree::dis(fa[i], x);
+= query(tree[fa[i]], r[x] - d);
ans -= query(sonTree[i], r[x] - d);
ans (sonTree[i], d - r[x]);
insert}
int d = Tree::dis(i, x);
(tree[i], d - r[x]);
insert}
int rebuildx = 0;
for (int i = x; fa[i]; i = fa[i])
if (tree[i]->siz > tree[fa[i]]->siz * 0.88)
= fa[i];
rebuildx if (rebuildx) rebuild(rebuildx);
return ans;
}
};
[N * 100];
Treap nodeint main() {
for (int i = 0; i < N * 100; ++i)
::bin.push(node + i);
Treapint n, a, cc, v;
("%*d%d", &n);
scanf= 0;
LL lastans [0] = -1;
prefor (int i = 1; i <= n; ++i) {
("%d%d%d", &a, &cc, &v);
scanf[i] = v;
r^= lastans % 1000000000;
a [i] = -1;
pre::insert(i, cc, a);
Tree+= Dynamic_TreeDivision::insert(i, a);
lastans ("%lld\n", lastans);
printf}
return 0;
}