【BZOJ4196】【UOJ128】【NOI2015】软件包管理器

【题目链接】

【思路要点】

  • 树链剖分,子树是DFS序中连续的一段,而路径由至多\(O(LogN)\)段组成,剩余部分用线段树解决即可。
  • 时间复杂度\(O(QLog^{2}N)\)。

【代码】

#include<bits/stdc++.h>
using namespace std; 
#define MAXN	120000
struct node {
	int l, r, mid, lc, rc, size, sum; 
	bool install, uninstall; 
}; 
int n, t, total; 
vector <int> a[MAXN]; 
int depth[MAXN], size[MAXN], father[MAXN], heavy_son[MAXN]; 
int new_num[MAXN], up[MAXN], right_num[MAXN]; 
node tree[MAXN * 2]; 
void push_down(int x) {
	if (tree[x].l == tree[x].r || tree[x].install == false && tree[x].uninstall == false) return; 
	if (tree[x].install) {
		tree[tree[x].lc].install = true; 
		tree[tree[x].lc].uninstall = false; 
		tree[tree[x].lc].sum = tree[tree[x].lc].size; 
		tree[tree[x].rc].install = true; 
		tree[tree[x].rc].uninstall = false; 
		tree[tree[x].rc].sum = tree[tree[x].rc].size; 
		tree[x].install = false; 
	} else {
		tree[tree[x].lc].uninstall = true; 
		tree[tree[x].lc].install = false; 
		tree[tree[x].lc].sum = 0; 
		tree[tree[x].rc].uninstall = true; 
		tree[tree[x].rc].install = false; 
		tree[tree[x].rc].sum = 0; 
		tree[x].uninstall = false; 
	}
}
void maintain(int x, int l, int r, int type) {
	if (type == 1) {
		if (tree[x].l == l && tree[x].r == r) {
			tree[x].install = true; 
			tree[x].uninstall = false; 
			tree[x].sum = tree[x].size; 
			return; 
		}
		push_down(x); 
		if (l <= tree[x].mid) maintain(tree[x].lc, l, min(r, tree[x].mid), type); 
		if (r > tree[x].mid) maintain(tree[x].rc, max(l, tree[x].mid + 1), r, type); 
		tree[x].sum = tree[tree[x].lc].sum + tree[tree[x].rc].sum; 
	} else {
		if (tree[x].l == l && tree[x].r == r) {
			tree[x].uninstall = true; 
			tree[x].install = false; 
			tree[x].sum = 0; 
			return; 
		}
		push_down(x); 
		if (l <= tree[x].mid) maintain(tree[x].lc, l, min(r, tree[x].mid), type); 
		if (r > tree[x].mid) maintain(tree[x].rc, max(l, tree[x].mid + 1), r, type); 
		tree[x].sum = tree[tree[x].lc].sum + tree[tree[x].rc].sum; 
	}
}
int query(int x, int l, int r) {
	if (tree[x].l == l && tree[x].r == r) return tree[x].sum; 
	push_down(x); 
	int ans = 0; 
	if (l <= tree[x].mid) ans += query(tree[x].lc, l, min(r, tree[x].mid)); 
	if (r > tree[x].mid) ans += query(tree[x].rc, max(l, tree[x].mid + 1), r); 
	return ans; 
}
void build(int x, int l, int r) {
	tree[x].l = l; 
	tree[x].r = r; 
	tree[x].size = r - l + 1; 
	tree[x].mid = (l + r) / 2; 
	if (l == r) return; 
	tree[x].lc = ++total; 
	build(total, l, tree[x].mid); 
	tree[x].rc = ++total; 
	build(total, tree[x].mid + 1, r); 
}
int Uninstall(int x) {
	int ans = query(1, new_num[x], right_num[x]); 
	maintain(1, new_num[x], right_num[x], 0); 
	return ans; 
}
int Install(int x) {
	int ans = depth[x]; 
	while (up[x] != 0) {
		ans -= query(1, new_num[up[x]], new_num[x]); 
		maintain(1, new_num[up[x]], new_num[x], 1); 
		x = up[x]; 
		x = father[x]; 
	}
	ans -= query(1, new_num[up[x]], new_num[x]); 
	maintain(1, new_num[up[x]], new_num[x], 1); 
	return ans; 
}
void work(int x) {
	if (x != heavy_son[father[x]]) up[x] = x; 
	else up[x] = up[father[x]]; 
	new_num[x] = ++t; 
	if (heavy_son[x] != 0) work(heavy_son[x]); 
	for (unsigned i = 0; i<a[x].size(); i++) {
		if (a[x][i] != heavy_son[x]) work(a[x][i]); 
	}
	right_num[x] = t; 
}
void dfs(int x) {
	int maxnum = 0; 
	depth[x] = depth[father[x]] + 1; 
	size[x] = 1; 
	for (unsigned i = 0; i < a[x].size(); i++) {
		dfs(a[x][i]); 
		size[x] += size[a[x][i]]; 
		if (size[a[x][i]] > maxnum) {
			maxnum = size[a[x][i]]; 
			heavy_son[x] = a[x][i]; 
		}
	}
}
int main() {
	int n; 
	scanf("%d", &n); 
	int i; 
	for (i = 1; i < n; i++) {
		scanf("%d", &father[i]); 
		a[father[i]].push_back(i); 
	}
	dfs(0); 
	work(0); 
	total = 1; 
	build(1, 1, n); 
	int x, q; 
	scanf("%d", &q); 
	char s[10]; 
	for (i = 1; i <= q; i++) {
		scanf("%s%d", s, &x); 
		if (s[0] == 'i') printf("%d\n", Install(x)); 
		else printf("%d\n", Uninstall(x)); 
	}
	return 0; 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值