[WC2014] 紫荆花之恋 (点分树)(替罪羊树)(点分树定期重构)

关于求答案

对于每一个分治中心 g g g,求经过它的点对 ( u , v ) (u,v) (u,v) 的贡献
需要 d i s ( u , g ) + d i s ( v , g ) ≤ r u + r v dis(u,g)+dis(v,g)\le r_u+r_v dis(u,g)+dis(v,g)ru+rv
也就是 d i s ( u , g ) − r u ≤ r v − d i s ( v , g ) dis(u,g)-r_u\le r_v-dis(v,g) dis(u,g)rurvdis(v,g)
可以用平衡树维护一波 d i s ( u , g ) − r u dis(u,g)-r_u dis(u,g)ru,然后就是查询比一个数小的个数
但这样是算重了的,因为 u , v u,v u,v 可能在 g g g 下方的同一棵子树
然后还需要维护一棵减去贡献的平衡树
不用想复杂,减去的贡献 ( u , v ) (u,v) (u,v) 当且仅当 ( u , v ) (u,v) (u,v) 在切掉 g g g 的情况下还在一个联通块
于是可以对这些连通块维护平衡树存同样的值
也就是说当前点 u u u 的贡献,就是在每一个祖先 g g g 查一个答案,然后在 g g g 割掉后 u u u 的联通块的平衡树中查一个答案减掉


为了避免点分树每次跳 f a fa fa 的繁琐讨论
我们每个点开一个 v e c t o r vector vector ( f a , d i s , t r e e ) (fa,dis,tree) (fa,dis,tree)
表示这个点的祖先,到祖先的距离以及切掉祖先后 u u u 所在的平衡树的编号


因为要插叶子,所以点分树会不平衡,类似替罪羊的思想,设一个平衡因子 α \alpha α,定期重构


重构点分树
首先需要知道支持这个重构操作需要维护什么?
子树需要清空,每个点开 v e c t o r vector vector 暴力维护子树中的结点
然后对于一个子孙的 ( f a , d i s , t r e e ) (fa,dis,tree) (fa,dis,tree) t r e e tree tree 我们需要把它清空,所以一个点要维护切掉它过后的 t r e e tree tree 的集合,同样 v e c t o r vector vector 暴力维护
需要清空的东西:
1.子树所有点的平衡树
2.子树所有点维护儿子的 v e c t o r vector vector
3.子树用来减的平衡树
4.子树所有维护祖先的 v e c t o r vector vector
5.维护割掉它剩下的联通块的平衡树

细节:发现祖先的 v e c t o r vector vector 是按顺序插的,深的在下面,于是弹一个点的祖先的时候可以暴力弹的 x x x


点分治
跟一般的点分治一样, d f s dfs dfs 的时候插一下维护祖先的 v e c t o r vector vector
然后暴力把 d f s dfs dfs 到的点插到自己的儿子集合,把 r i − d i s r_i-dis ridis 存下来平衡树 b u i l d build build 一波
然后还要维护一圈的平衡树,把那一棵子树的点弄出来暴力 b u i l d build build 就可以了


插入
x x x 挂在 u u u
先把 u u u 维护祖先的 v e c t o r vector vector 复制给 x x x,然后将里面的每一个 d i s dis dis 加上 d i s ( x , u ) dis(x,u) dis(x,u)
在祖先 g g g 的平衡树中插入 d i s − r x dis - r_x disrx 的值
然后需要在 x x x v e c t o r vector vector 中插入 ( u , d i s ( x , u ) , t r e e ) (u,dis(x,u),tree) (u,dis(x,u),tree) 这个三元组
然后暴力跳祖先,把 x x x 加到它们的集合
中途遇到不平衡就找到最高的并重构


总结
再次理清思路:

  1. 查询答案需要维护平衡树,同时需要容斥掉在一个子树不合法的情况,所以需要维护一个点割掉它剩下的一圈平衡树
  2. 一个点要查答案需要知道所有祖先,祖先的平衡树,祖先割掉后 x x x 属于的那个连通块的平衡树,用一个 v e c t o r vector vector
  3. 一个点要重构整个子树需要知道子树的所有点, v e c t o r vector vector 暴力维护,插入时更新

主要细节就是这些,第一个特判
平衡树随便选一个就可以了


#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
typedef long long ll;
cs double alpha = 0.8;
cs int N = 2e5 + 5, M = 6e6 + 5;
cs int mod = 1e9;
int TEST, n, r[N]; 
ll ans;
namespace SBT{ // 替罪羊  
	int ch[M][2], fa[M], siz[M], val[M]; int node; 
	int sta[M], top;
	int a[M], hd; 
	#define ls ch[x][0]
	#define rs ch[x][1]
	int ck(){ return top ? sta[top] : node + 1; }
	int newnode(){ return top ? sta[top--] : ++node; }
	void bin(int x){ ls = rs = 0; sta[++top] = x;} 
	void pia(int x){ if(ls) pia(ls); if(rs) pia(rs); bin(x); }
	int gc(int x){ return ch[fa[x]][1] == x; }
	struct sbt{
		int rt; 
		void re(int x){ if(ls) re(ls); a[++hd] = x; if(rs) re(rs); }
		void pushup(int x){ siz[x] = siz[ls] + siz[rs] + 1; }
		int build(int l, int r){
			if(l > r) return 0; int mid = (l+r) >> 1, x = a[mid];
			ls = build(l, mid - 1); rs = build(mid + 1, r); 
			fa[ls] = fa[rs] = x; pushup(x); return x;
		}
		void ins(int v){
			int hi = -1, p = 0;
			for(int x = rt; x; x = ch[p][val[p] <= v]){
				p = x; ++siz[p]; if(fa[p] && fa[p] != rt && hi == -1 && siz[fa[p]] * 4 <= siz[p] * 5) hi = fa[p];
			} int nx = newnode(); fa[nx] = p; ch[p][val[p] <= v] = nx; val[nx] = v; siz[nx] = 1;
			// rebuild  
			if(hi != -1){ int f = fa[hi]; int k = gc(hi); hd = 0; re(hi); ch[f][k] = build(1, hd); fa[ch[f][k]] = f; }
		}
		int query(int v){
			int ans = 0; for(int x = rt; x;){
				if(val[x] <= v) ans += siz[ls] + 1, x = rs;
				else x = ls; 
			} return ans;
		} 
		int rebuild(int *a, int l, int r){
			if(l > r) return 0; int mid = (l+r) >> 1; int x = newnode(); val[x] = a[mid]; 
			ls = rebuild(a, l, mid - 1); rs = rebuild(a, mid + 1, r); 
			fa[ls] = fa[rs] = x; pushup(x); return x;
		}
		void clear(){pia(rt);}
		void INIT(int *a, int len){ rt = rebuild(a, 0, len); fa[rt] = 0;  } 
	};
}
namespace TREE{
	int first[N], nxt[N], to[N], w[N], tot;
	void add(int x, int y, int z){
		nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
	}
	int ndep[N];
	vector<int> son[N];
	struct data{
		int fa, dis; SBT::sbt T;
	}; vector<data> v[N];
	typedef vector<data>::iterator Data;
	typedef vector<int>::iterator Int;
	typedef vector<SBT::sbt>::iterator Sbt;
	vector<SBT::sbt> nw[N];
	SBT::sbt T[N];
	int top, *mdep, ret;
	int siz[N], mxson[N];
	bool cut[N];
	int rt;
	
	void getsz(int u, int fa){
		siz[u] = 1;
		for(int i = first[u]; i; i = nxt[i]){
			int t = to[i]; if(t == fa || !cut[t]) continue;
			getsz(t, u); siz[u] += siz[t];
		} 
	}
	
	void getrt(int u, int fa, int S){
		mxson[u] = 0;
		for(int i = first[u]; i; i = nxt[i]){
			int t = to[i]; if(t == fa || !cut[t]) continue;
			getrt(t, u, S); mxson[u] = max(mxson[u], siz[t]);
		} mxson[u] = max(mxson[u], S - siz[u]);
		if(mxson[rt] > mxson[u]) rt = u;
	}
	
	void dfs(int u, int fa, int dis, int g, int p){
		son[g].push_back(u); v[u].push_back((data){g, dis, (SBT::sbt){p}}); 
		mdep[++top] = dis - r[u];
		for(int i = first[u]; i; i = nxt[i]){
			int t = to[i]; if(t == fa || !cut[t]) continue;
			dfs(t, u, dis + w[i], g, p);
		}
	}
	void solve(int x){
		getsz(x, 0); rt = 0; getrt(x, 0, siz[x]); int g = rt; cut[g] = false; 
		son[g].clear(); son[g].push_back(g);
		if(siz[x] == 1){ ndep[0] = -r[x]; T[g].INIT(ndep, 0); return; } 
		mdep = ndep; top = -1; int Siz = 0;
		for(int i = first[g]; i; i = nxt[i]){
			if(!cut[to[i]]) continue; mdep = mdep + top + 1;
			Siz += top + 1;
			top = -1;
			dfs(to[i], g, w[i], g, SBT::ck()); 
			SBT::sbt tr; sort(mdep, mdep + top + 1);
			tr.INIT(mdep, top); nw[g].push_back(tr);
		} Siz += top + 1; ndep[Siz] = -r[g]; sort(ndep, ndep + Siz + 1); 
		T[g].INIT(ndep, Siz);
		for(int i = first[g]; i; i = nxt[i]) if(cut[to[i]]) solve(to[i]);
	}
	void rebuild(int x){
		for(Int it = son[x].begin(); it != son[x].end(); it++) cut[*it] = true;
		for(Int it = son[x].begin(); it != son[x].end(); it++) T[*it].clear(); // 清空子树的平衡树  
		for(Int it = son[x].begin(); it != son[x].end(); it++){ // 	清空子树所有点周围的平衡树  
			for(Sbt it1 = nw[*it].begin(); it1 != nw[*it].end(); it1++){
				it1->clear(); 
			} nw[*it].clear();
		}
		for(Int it = son[x].begin(); it != son[x].end(); it++){ // 清空子树中所有点存的祖先  
			if(*it != x){
				while(1){
					if(v[*it].rbegin()->fa == x){ // fa 是按顺序插入的  
						v[*it].pop_back(); break;
					}  else v[*it].pop_back();
				}
			}
		} solve(x); 
	}
	
	int ins(int x, int fa, int d, int wi){
		add(x, fa, d); add(fa, x, d);
		v[x] = v[fa];
		for(Data it = v[x].begin(); it != v[x].end(); it++){
			it->dis += d;
			it->T.ins(it->dis - wi); 
			T[it->fa].ins(it->dis - wi);
		}
		SBT::sbt tr;
		ndep[0] = d - wi;
		tr.INIT(ndep, 0);
		v[x].push_back((data){fa, d, tr});
		nw[fa].push_back(tr);
		T[fa].ins(d - wi);
		ndep[0] = - wi;
		T[x].INIT(ndep, 0);
		son[x].push_back(x);
		for(Data it = v[x].begin(); it != v[x].end(); ++it) son[it->fa].push_back(x);
		Data it1 = v[x].begin(), it2 = it1; ++it2;
		for(;it2 != v[x].end(); ++it2, ++it1){
			if(son[it2->fa].size() * 5 >= son[it1->fa].size() * 4){ rebuild(it1->fa); break; }
		}	
		int ans = T[x].query(wi) - 1; // 到 p 的点单独查  
		for(Data it = v[x].begin(); it != v[x].end(); ++it) 
			ans += T[it->fa].query(wi - it->dis) - it->T.query(wi - it->dis);
		return ans;
	}
	void INIT(int wi){
		ndep[0] = -wi; T[1].INIT(ndep, 0); son[1].push_back(1);
	}
}
int main(){
	TEST = read();
	n = read();
	TREE::mxson[0] = n + 1; 
	for(int i = 1; i <= n; i++){
		ll fa = (read() ^ (ans % mod)), d = read(); r[i] = read();
		if(i == 1){ TREE::INIT(r[i]); puts("0"); continue; }
		ans += (ll)TREE::ins(i, fa, d, r[i]); cout << ans << '\n';
	} return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值