【BZOJ3435】【UOJ55】【WC2014】紫荆花之恋

【题目链接】

【思路要点】

  • 考虑点\(i\)和点\(j\)路径上任意一点\(k\),那么$$R_{i}+R_{j}≥Dist(i,j) \Leftrightarrow R_{i}-Dist(i,k)≥Dist(j,k)-R_{j}$$
  • 令\(F_{i,k}=R_{i}-Dist(i,k)\),即\(F_{i,k}≥-F_{j,k}\)
  • 若原树不动,显然可以通过点分治配合排序+二分的方式在\(O(NLog^{2}N)\)的时间内求解点对个数。
  • 若不要求算法在线,而是提前预知树的形态,我们可以先将点分树构建出来,然后在每一个点分树的节点\(k\)上维护一棵子树内以\(F_{i,k}(i \in k's\ Subtree)\)为关键字的平衡树。在生长出叶子时,在对应的\(O(LogN)\)棵平衡树内插入\(F_{i,k}\)后依次询问\(≥-F_{i,k}\)的数的个数。同时,为了避免重复统计,在点分树上的每一个子节点\(k\)上还需要维护一棵以\(F_{i,f}(i \in k's\ Subtree)\)为关键字的平衡树,其中\(f\)为\(k\)在点分树上的父亲,询问时,应当减去有关这类平衡树的询问结果。同样可以获得\(O(NLog^{2}N)\)的时间复杂度。
  • 那么,要求算法在线,即分治结构不确定的情况如何处理呢?学过替罪羊树的读者应该知道,替罪羊树维护平衡的方式不同于一般的基于旋转的平衡树,而是基于暴力重构,其重构的总复杂度是\(O(N)\)的。我们发现,若将分治结构的改变看做在替罪羊树中插入节点,那么,当替罪羊树不再平衡时,我们只需将对应子树重新进行一次点分治即可。
  • 即当\(Max_{j\in i's\ Subtree}\{Size_{j}\}≥Size_{i}+1\),则对\(i\)为根的点分子树重新分治。
  • 如此来看,重构的复杂度为\(O(NLog^{2}N)\),插入和询问的复杂度也为\(O(NLog^{2}N)\),故整体时间复杂度为\(O(NLog^{2}N)\),可以通过本题。注意在重新分治时的内存回收,否则容易空间超限。笔者的平衡因子\(\alpha\)取\(\alpha=0.8\),分治中所用的平衡树同样是替罪羊树。
  • 实现细节较多,容易写错,笔者共用了\(3h+\)才在OJ上通过了本题。

【代码】

#include<bits/stdc++.h>
using namespace std;
#define MAXP	10000005
#define MAXN	100005
#define MAXLOG	105
#define ALPHA	0.8
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct DivideAndConquerTree {
	struct ScapeGoatTree {
		struct Node {
			int index, size;
			int lc, rc;
		} a[MAXP];
		int SonRoot[MAXN];
		int RootRoot[MAXN];
		int top, mem[MAXP];
		int tot, tmp[MAXN];
		void init(int tot) {
			top = 0;
			for (int i = tot; i >= 1; i--)
				mem[++top] = i;
		}
		int new_node(int val) {
			int tmp = mem[top--];
			a[tmp] = (Node) {val, 1, 0, 0};
			return tmp;
		}
		void dfs(int pos) {
			mem[++top] = pos;
			if (a[pos].lc) dfs(a[pos].lc);
			tmp[++tot] = a[pos].index;
			if (a[pos].rc) dfs(a[pos].rc);
		}
		int rebuild(int l, int r) {
			if (l == r) return new_node(tmp[l]);
			int mid = (l + r) / 2, ans = new_node(tmp[mid]);
			a[ans].size = r - l + 1;
			if (mid > l) a[ans].lc = rebuild(l, mid - 1);
			if (mid < r) a[ans].rc = rebuild(mid + 1, r);
			return ans;
		}
		int Rebuild(int root, int val) {
			tot = 0, dfs(root);
			bool flg = true;
			for (int i = 1; i <= tot; i++)
				if (tmp[i] >= val) {
					tot++;
					for (int j = tot; j >= i + 1; j--)
						tmp[j] = tmp[j - 1];
					tmp[i] = val;
					flg = false;
					break;
				}
			if (flg) tmp[++tot] = val;
			return rebuild(1, tot);
		}
		void insert(int &pos, int val) {
			if (pos == 0) {
				pos = new_node(val);
				return;
			}
			a[pos].size++;
			if (val < a[pos].index) {
				if (a[a[pos].lc].size + 1 > a[pos].size * ALPHA + 1) {
					pos = Rebuild(pos, val);
				} else insert(a[pos].lc, val);
			} else {
				if (a[a[pos].rc].size + 1 > a[pos].size * ALPHA + 1) {
					pos = Rebuild(pos, val);
				} else insert(a[pos].rc, val);
			}
		}
		void restore(int pos) {
			mem[++top] = pos;
			if (a[pos].lc) restore(a[pos].lc);
			if (a[pos].rc) restore(a[pos].rc);
		}
		int query(int pos, int val) {
			if (pos == 0) return 0;
			if (val < a[pos].index) return query(a[pos].lc, val);
			else return query(a[pos].rc, val) + a[a[pos].lc].size + 1;
		}
		void RootClear(int pos) {
			if (RootRoot[pos]) restore(RootRoot[pos]);
			RootRoot[pos] = 0;
		}
		void SonClear(int pos) {
			if (SonRoot[pos]) restore(SonRoot[pos]);
			SonRoot[pos] = 0;
		}
		void RootInsert(int pos, int val) {
			insert(RootRoot[pos], val);
		}
		void SonInsert(int pos, int val) {
			insert(SonRoot[pos], val);
		}
		int RootQuery(int pos, int val) {
			return query(RootRoot[pos], val);
		}
		int SonQuery(int pos, int val) {
			return query(SonRoot[pos], val);
		}
		void give(int from, int to) {
			SonRoot[to] = SonRoot[from];
			SonRoot[from] = 0;
		}
	} SGT;
	struct Tree {
		int depth[MAXN], father[MAXN];
		int dist[MAXN][MAXLOG], size[MAXN];
		unsigned home[MAXN];
		vector <int> a[MAXN];
	} T;
	struct edge {int dest, len; };
	int n, cnt, root, r[MAXN];
	vector <edge> a[MAXN];
	int size[MAXN], weight[MAXN];
	bool mark[MAXN];
	void chkmax(int &x, int y) {
		x = max(x, y);
	}
	void init(int x, int y) {
		T.a[0].push_back(0);
		T.depth[1] = 1;
		n = x; r[1] = y;
		weight[0] = n + 1;
		SGT.init(MAXP - 1);
		SGT.RootInsert(1, -y);
	}
	void dfs(int pos) {
		cnt++;
		mark[pos] = true;
		for (unsigned i = 0; i < T.a[pos].size(); i++)
			dfs(T.a[pos][i]);
	}
	void getroot(int pos, int fa, int cnt) {
		size[pos] = 1; weight[pos] = 0;
		for (unsigned i = 0; i < a[pos].size(); i++)
			if (a[pos][i].dest != fa && mark[a[pos][i].dest]) {
				getroot(a[pos][i].dest, pos, cnt);
				size[pos] += size[a[pos][i].dest];
				chkmax(weight[pos], size[a[pos][i].dest]);
			}
		chkmax(weight[pos], cnt - size[pos]);
		if (weight[pos] < weight[root]) root = pos;
	}
	void recalsize(int pos, int fa) {
		size[pos] = 1;
		for (unsigned i = 0; i < a[pos].size(); i++)
			if (a[pos][i].dest != fa && mark[a[pos][i].dest]) {
				recalsize(a[pos][i].dest, pos);
				size[pos] += size[a[pos][i].dest];
			}
	}
	void recal(int pos, int fa, int from, int fromdepth, int dist) {
		T.size[from]++;
		T.dist[pos][fromdepth] = dist;
		SGT.RootInsert(from, dist - r[pos]);
		SGT.SonInsert(root, dist - r[pos]);
		for (unsigned i = 0; i < a[pos].size(); i++)
			if (a[pos][i].dest != fa && mark[a[pos][i].dest]) {
				recal(a[pos][i].dest, pos, from, fromdepth, dist + a[pos][i].len);
			}
	}
	void work(int pos, int depth, int tot) {
		T.a[pos].clear();
		mark[pos] = false;
		T.depth[pos] = depth;
		SGT.RootClear(pos);
		SGT.RootInsert(pos, -r[pos]);
		recalsize(pos, 0);
		T.size[pos] = 1;
		T.dist[pos][depth] = 0;
		for (unsigned i = 0; i < a[pos].size(); i++)
			if (mark[a[pos][i].dest]) {
				root = 0;
				getroot(a[pos][i].dest, 0, size[a[pos][i].dest]);
				SGT.SonClear(root);
				recal(a[pos][i].dest, pos, pos, depth, a[pos][i].len);
			}
		for (unsigned i = 0; i < a[pos].size(); i++)
			if (mark[a[pos][i].dest]) {
				root = 0;
				getroot(a[pos][i].dest, 0, size[a[pos][i].dest]);
				T.father[root] = pos;
				T.a[pos].push_back(root);
				T.home[root] = T.a[pos].size() - 1;
				work(root, depth + 1, size[a[pos][i].dest]);
			}
	}
	void rebuild(int pos) {
		cnt = 0;
		dfs(pos);
		root = 0;
		getroot(pos, 0, cnt);
		T.a[T.father[pos]][T.home[pos]] = root;
		T.father[root] = T.father[pos];
		SGT.give(pos, root);
		work(root, T.depth[pos], cnt);
	}
	int insert(int pos, int x, int y, int z) {
		a[pos].push_back((edge) {x, y});
		a[x].push_back((edge) {pos, y});
		r[pos] = z;
		T.size[pos] = 1;
		T.father[pos] = x;
		T.a[x].push_back(pos);
		T.depth[pos] = T.depth[x] + 1;
		T.home[pos] = T.a[x].size() - 1;
		T.dist[pos][T.depth[pos]] = 0;
		SGT.RootInsert(pos, -z);
		int now = pos, last = now, Reroot = 0;
		for (int i = T.depth[pos] - 1; i >= 1; i--) {
			last = now;
			now = T.father[now];
			T.size[now]++;
			T.dist[pos][i] = T.dist[x][i] + y;
			SGT.RootInsert(now, T.dist[pos][i] - z);
			SGT.SonInsert(last, T.dist[pos][i] - z);
			if (T.size[last] > T.size[now] * ALPHA + 1) Reroot = now;
		}
		if (Reroot) rebuild(Reroot);
		int ans = SGT.RootQuery(pos, z) - 1;
		now = pos, last = now;
		for (int i = T.depth[pos] - 1; i >= 1; i--) {
			last = now;
			now = T.father[now];
			ans += SGT.RootQuery(now, z - T.dist[pos][i]);
			ans -= SGT.SonQuery(last, z - T.dist[pos][i]);
		}
		return ans;
	}
} DACT;
int main() {
	int t; read(t);
	int n; read(n);
	long long lastans = 0;
	read(t), read(t), read(t);
	DACT.init(n, t);
	writeln(lastans);
	for (int i = 2; i <= n; i++) {
		int x, y, z;
		read(x), read(y), read(z);
		x ^= (lastans % 1000000000);
		lastans += DACT.insert(i, x, y, z);
		writeln(lastans);
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值