bzoj3435[Wc2014]紫荆花之恋 替罪点分树套SBT
Posted GXZlegend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj3435[Wc2014]紫荆花之恋 替罪点分树套SBT相关的知识,希望对你有一定的参考价值。
题目描述
强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点。每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离 dist(i,j) ≤ Ri + R! ,其中 dist(i, j)表示在这个树上从 i 到 j 的唯一路径上所有边的边权和。强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。
我们假定这个树一开始为空,节点按照加入的顺序从 1开始编号。由于强强非常好奇, 你必须在他每次出现新节点后马上给出总共的朋友对数,不能拖延哦。
输入
共有 n + 2 行。
第一行包含一个正整数,表示测试点编号。
第二行包含一个正整数 n ,表示总共要加入的节点数。
我们令加入节点前的总共朋友对数是 last_ans,在一开始时它的值为0。
接下来 n 行中第 i 行有三个数 ai, bi, ri,表示节点 i 的父节点的编号为 ai xor (last_ans mod 10^9) (其中xor 表示异或,mod 表示取余,数据保证这样操作后得到的结果介于 1到i – 1之间),与父节点之间的边权为 ci,节点 i 上小精灵的感受能力值为r!。
注意 a1 = c1 = 0,表示 1 号点是根节点,对于 i > 1,父节点的编号至少为1。
输出
包含 n 行,每行输出1 个整数, 表示加入第 i 个点之后,树上有几对朋友。
样例输入
0
5
0 0 6
1 2 4
0 9 4
0 5 5
0 2 4
样例输出
0
1
2
4
7
题解
替罪点分树套SBT
如果不考虑插入新点位置(即可以离线把树建出来),只考虑查询的话,那么就相当于查询$dis(i,j)\ge r_i+r_j$,即$dis(i,j)-r_j\ge r_i$的点$j$有多少个。
考虑点分治,一个点的子树中某子节点子树以外的节点到该子节点子树中某点的距离,可以看作先到根节点,再从根节点到这个点。所以可以使用容斥的方法,用所有的点减去在该子树内的点,维护某子树中所有点的$dis(x,rt)-r$,和某子树中所有点的$dis(x,fa[rt])-r$,在查找时,即找出$dis-r\ge r_i-dis(i,rt)$的所有点,于是使用平衡树SBT维护这两个信息,并在平衡树里查找即可。
然而本题强制在线,因此不能把整棵点分树建出来然后处理询问。
这时就可以使用替罪羊树“替罪”的思想,当距离偏差大的时候暴力重构。这样就可以维护一个近似平衡的分治结构以处理询问。
对于每个加点操作,直接把它挂到父亲节点之下,并向上更新子树信息。如果失衡,则找到最上端的替罪节点,重构这棵树。由于点分树的子树就是原树的连通块结构,因此可以直接对这部分建出点分树以重构。
实现起来稍微有点复杂,可以使用vector什么的减小代码复杂度。
查询时按照上面的方法查询即可,需要使用倍增LCA求两点距离。
时间复杂度$O(n\log^2n)=O(能过)$
还是需要内存回收之类的保证空间复杂度。
#include <queue> #include <cstdio> #include <vector> #include <algorithm> #define N 100010 using namespace std; struct sbt { int w , ls , rs , si; }a[20000010]; queue<int> q; vector<int> c[N]; int head[N] , to[N << 1] , next[N << 1] , cnt , fa[N][20] , len[N] , deep[N] , log[N]; int r[N] , pre[N] , si[N] , mx[N] , sum , root , ra[N] , rb[N] , vis[N]; long long ans; void add(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } void zig(int &k) { int t = a[k].ls; a[k].ls = a[t].rs , a[t].rs = k , a[t].si = a[k].si , a[k].si = a[a[k].ls].si + a[a[k].rs].si + 1; k = t; } void zag(int &k) { int t = a[k].rs; a[k].rs = a[t].ls , a[t].ls = k , a[t].si = a[k].si , a[k].si = a[a[k].ls].si + a[a[k].rs].si + 1; k = t; } void maintain(int &k , bool flag) { if(!flag) { if(a[a[a[k].ls].ls].si > a[a[k].rs].si) zig(k); else if(a[a[a[k].ls].rs].si > a[a[k].rs].si) zag(a[k].ls) , zig(k); else return; } else { if(a[a[a[k].rs].rs].si > a[a[k].ls].si) zag(k); else if(a[a[a[k].rs].ls].si > a[a[k].ls].si) zig(a[k].rs) , zag(k); else return; } maintain(a[k].ls , 0) , maintain(a[k].rs , 1); maintain(k , 0) , maintain(k , 1); } void insert(int &k , int x) { if(!k) k = q.front() , q.pop() , a[k].w = x , a[k].si = 1; else { a[k].si ++ ; if(x < a[k].w) insert(a[k].ls , x); else insert(a[k].rs , x); maintain(k , x >= a[k].w); } } int find(int k , int x) { if(!k) return 0; else if(x <= a[k].w) return find(a[k].ls , x) + a[a[k].rs].si + 1; else return find(a[k].rs , x); } void del(int &k) { if(!k) return; del(a[k].ls) , del(a[k].rs); a[k].w = a[k].si = 0 , q.push(k) , k = 0; } int lca(int x , int y) { int i; if(len[x] < len[y]) swap(x , y); for(i = log[len[x] - len[y]] ; ~i ; i -- ) if(len[x] - len[y] >= (1 << i)) x = fa[x][i]; if(x == y) return x; for(i = log[len[x]] ; ~i ; i -- ) if(len[x] >= (1 << i) && fa[x][i] != fa[y][i]) x = fa[x][i] , y = fa[y][i]; return fa[x][0]; } int dis(int x , int y) { return deep[x] + deep[y] - 2 * deep[lca(x , y)]; } int query(int x) { int i , ans = 0; for(i = x ; i ; i = pre[i]) ans += find(ra[i] , dis(x , i) - r[x]); for(i = x ; pre[i] ; i = pre[i]) ans -= find(rb[i] , dis(x , pre[i]) - r[x]); return ans; } void dfs(int x , int fa , int p) { int i; si[x] = 1 , c[p].push_back(x) , insert(ra[p] , r[x] - dis(x , p)); if(pre[p]) insert(rb[p] , r[x] - dis(x , pre[p])); for(i = head[x] ; i ; i = next[i]) if(!vis[to[i]] && to[i] != fa) dfs(to[i] , x , p) , si[x] += si[to[i]]; } void getroot(int x , int fa) { int i; si[x] = 1 , mx[x] = 0; for(i = head[x] ; i ; i = next[i]) if(!vis[to[i]] && to[i] != fa) getroot(to[i] , x) , si[x] += si[to[i]] , mx[x] = max(mx[x] , si[to[i]]); mx[x] = max(mx[x] , sum - si[x]); if(mx[x] < mx[root]) root = x; } void solve(int x , int from) { int i; del(ra[x]) , del(rb[x]) , c[x].clear(); pre[x] = from , vis[x] = 1 , dfs(x , 0 , x); for(i = head[x] ; i ; i = next[i]) if(!vis[to[i]]) sum = si[to[i]] , root = 0 , getroot(to[i] , 0) , solve(root , x); } void join(int x) { int i , last = -1; for(i = x ; i ; i = pre[i]) { c[i].push_back(x) , insert(ra[i] , r[x] - dis(x , i)); if(pre[i]) insert(rb[i] , r[x] - dis(x , pre[i])); si[i] ++ ; if(pre[i] && si[i] * 4 > (si[pre[i]] + 1) * 3) last = pre[i]; } if(~last) { for(i = 0 ; i < (int)c[last].size() ; i ++ ) vis[c[last][i]] = 0; sum = si[last] , root = 0 , getroot(last , 0) , solve(root , pre[last]); } } int main() { int n , i , j , f , b; for(i = 1 ; i <= 20000000 ; i ++ ) q.push(i); scanf("%*d%d%*d%*d%d" , &n , &r[1]); puts("0"); insert(ra[1] , r[1]) , c[1].push_back(1) , si[1] = vis[1] = 1 , mx[0] = 1 << 30; for(i = 2 ; i <= n ; i ++ ) { scanf("%d%d%d" , &f , &b , &r[i]) , f ^= (ans % 1000000000) , log[i] = log[i >> 1] + 1 , vis[i] = 1; fa[i][0] = pre[i] = f , len[i] = len[f] + 1 , deep[i] = deep[f] + b; add(f , i) , add(i , f); for(j = 1 ; (1 << j) <= len[i] ; j ++ ) fa[i][j] = fa[fa[i][j - 1]][j - 1]; ans += query(i) , printf("%lld\n" , ans); join(i); } return 0; }
以上是关于bzoj3435[Wc2014]紫荆花之恋 替罪点分树套SBT的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ3435 & 洛谷3920 & UOJ55:[WC2014]紫荆花之恋