题解:P7952 [✗✓OI R1] 天动万象

题意:很简单了,不再赘述。

做法:

首先 \(O(n^2)\) 暴力是显然的。

我们考虑怎么去优化这个暴力,对于一个节点的儿子全部贡献到其这种操作其实非常经典,比如动态 dp 的时候我们就采取了重链剖分的方式优化,我们考虑也去用链剖分的方式去优化。

具体的,我们每个节点钦定一个重儿子。那么对于一次修改,我们就将所有的链向上位移一位,对于链顶,直接暴力贡献到其父亲上,这个显然是可以用平衡树维护每一条链的,需要注意对于 \(u\) 节点所在的链,应该先 split 出来末尾若干个点再向上拉,链顶的权值应该直接扔掉。

注意在每个链消除完的时候,我们直接扔掉这个链,因为他以后一定不会有任何贡献了,这也是我们后面会提到的时间复杂度分析中的保证,这个可以用 set 去维护所有链的链顶。注意实现时按照深度或者 dfn 序对链进行修改,否则可能提前贡献出问题。

对于询问,我们考虑开一个线段树,线段树上每个叶子节点如果是链顶,那么权值为这条链的最大值,否则为 \(0\),那么就是在 dfs 序上的区间 \(\max\),当然对于 \(u\) 所在的链是残缺的,我们也去 split 出来贡献到答案里再 merge 回去。

那么聪明的你会发现了,我们做了这么久口胡,貌似没有提到该如何剖分,其他题解中都是采用的重链剖分,我没太看懂,我们来从头分析一下这个做法的时间消耗。

首先对于残缺的链,这一部分的修改次数显然是 \(O(n)\) 的,那么对于所有向上贡献的如何计算?我们考虑在每个轻边处统计,那么对于一个子树内向上的次数就是子树内的最长链长度,我们考虑每个点钦定一个重儿子,那么我们肯定是令儿子中最长链最长的,也就是长链剖分!并且我们很容易计算出来这样剖分的修改次数:对于每个链顶,也就是轻边下面的那个点,他的贡献是长链链长,即 \(O(n)\)

所以总复杂度为 \(O(n\log n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
mt19937 rnd(time(0));
const int maxn = 1e6 + 5;
int f[maxn];
vector<int> e[maxn];
int mxd[maxn], son[maxn], a[maxn], n, q, dfn[maxn], tot, top[maxn], rev[maxn], dep[maxn], ed[maxn];
struct node {
	int l, r;
	long long val;
	int sz, del;
	long long mx;
};
struct FHQ_Treap {
	node tr[maxn];
	int tot, rt[maxn];
	inline int newnode(int x) {
		tot++;
		tr[tot].val = tr[tot].mx = x, tr[tot].sz = 1, tr[tot].del = rnd();
		return tot;
	}
	inline void pushup(int t) {
		tr[t].sz = tr[tr[t].l].sz + tr[tr[t].r].sz + 1;
		tr[t].mx = max(max(tr[tr[t].l].mx, tr[tr[t].r].mx), tr[t].val);
	}
	void split(int p, int &l, int &r, int k) {
		if(!p) {
			l = r = 0;
			return ;
		}
		if(tr[tr[p].l].sz + 1 <= k)
			l = p, split(tr[p].r, tr[l].r, r, k - tr[tr[p].l].sz - 1);
		else
			r = p, split(tr[p].l, l, tr[r].l, k);
		pushup(p);
	}
	int mrg(int l, int r) {
		if(!l || !r)
			return l + r;
		int p;
		if(tr[l].del > tr[r].del)
			p = l, tr[l].r = mrg(tr[l].r, r);
		else
			p = r, tr[r].l = mrg(l, tr[r].l);
		pushup(p);
		return p;
	}
	inline void renew(int x, int k, long long val) {
		if(tr[tr[x].l].sz + 1 == k) {
			tr[x].val += val;
			pushup(x);
			return ;
		}
		if(tr[tr[x].l].sz + 1 < k)
			renew(tr[x].r, k - tr[tr[x].l].sz - 1, val);
		else
			renew(tr[x].l, k, val);
		pushup(x);
	}
	inline long long query(int x, int pos) {
		int p1, p2;
		split(rt[x], p1, p2, pos - 1);
		long long val = tr[p2].mx;
		rt[x] = mrg(p1, p2);
		return val;
	}
	inline void change(int x, int pos) {
		if(pos != -1) {
			int p1, p2, p3;
			split(rt[x], p1, p2, pos - 1);
			split(p2, p2, p3, 1);
			tr[p2].val = tr[p2].mx = 0;
			rt[x] = mrg(p1, mrg(p3, p2));
		}
		else {
			int p1, p2;
			split(rt[x], p1, p2, 1);
			long long v = tr[p1].val;
			tr[p1].val = tr[p1].mx = 0;
			rt[x] = mrg(p2, p1);
			renew(rt[top[f[x]]], dep[f[x]] - dep[top[f[x]]] + 1, v);
		}
	}
} tree;
void dfs1(int u, int fa) {
	dep[u] = dep[fa] + 1;
	mxd[u] = 1;
	for (int i = 0; i < e[u].size(); i++) {
		int v = e[u][i];
		if(v == fa)
			continue;
		dfs1(v, u);
		if(mxd[son[u]] < mxd[v])
			son[u] = v;	
		mxd[u] += mxd[v];
	}
}
void dfs2(int u, int t) {
	dfn[u] = ++tot; top[u] = t;
	rev[tot] = u;
	if(u != t)
		tree.rt[t] = tree.mrg(tree.rt[t], tree.rt[u]);
	if(!son[u]) {
		ed[u] = tot;
		return ;
	}
	dfs2(son[u], t);
	for (int i = 0; i < e[u].size(); i++) {
		int v = e[u][i];
		if(v == son[u])
			continue;
		dfs2(v, v);
	}
	ed[u] = tot;
}
set<int> s;
struct Segtree {
	long long tr[maxn << 2];
	inline void pushup(int t) {
		tr[t] = max(tr[t << 1], tr[t << 1 | 1]);
	}
	void build(int l, int r, int t) {
		if(l == r) {
			if(top[rev[l]] == rev[l])
				tr[t] = tree.tr[tree.rt[rev[l]]].mx;
			return ;
		}
		int mid = l + r >> 1;
		build(l, mid, t << 1), build(mid + 1, r, t << 1 | 1);
		pushup(t);
	}
	void modify(int l, int r, int pos, int t, long long val) {
		if(l == r) {
			tr[t] = val;
			return ;
		}
		int mid = l + r >> 1;
		if(pos <= mid)
			modify(l, mid, pos, t << 1, val);
		else
			modify(mid + 1, r, pos, t << 1 | 1, val);
		pushup(t);
	}
	long long query(int l, int r, int x, int y, int t) {
		if(x <= l && r <= y)
			return tr[t];
		int mid = l + r >> 1;
		if(y <= mid)
			return query(l, mid, x, y, t << 1);
		if(mid < x)
			return query(mid + 1, r, x, y, t << 1 | 1);
		return max(query(l, mid, x, y, t << 1), query(mid + 1, r, x, y, t << 1 | 1)); 
	}
} tree2;
inline long long query(int x) {
	long long ans = tree2.query(1, n, dfn[x], ed[x], 1);
	return max(ans, tree.query(top[x], dep[x] - dep[top[x]] + 1));
}
queue<int> ers;
inline void change(int x) {
	tree.change(top[x], dep[x] - dep[top[x]] + 1);
	set<int>::iterator itl = s.lower_bound(dfn[x] + 1), itr = s.upper_bound(ed[x]);
	for (set<int>::iterator it = itl; it != itr; it++) 
		tree.change(rev[*it], -1);
	if(tree.tr[tree.rt[top[x]]].mx == 0)
		ers.push(dfn[top[x]]);
	tree2.modify(1, n, dfn[top[x]], 1, tree.tr[tree.rt[top[x]]].mx);
	for (set<int>::iterator it = itl; it != itr; it++) {
		long long v = tree.tr[tree.rt[rev[*it]]].mx;
		tree2.modify(1, n, *it, 1, v);
		if(!v)
			ers.push(*it);
	}
	while(!ers.empty())
		s.erase(ers.front()), ers.pop();
}
inline int read() {
	int sum = 0;
	char c = getchar();
	while(!isdigit(c))
		c = getchar();
	while(isdigit(c))
		sum = sum * 10 + c - '0', c = getchar();
	return sum;
}
void write(long long x) {
	if(x <= 9) {
		putchar(x + '0');
		return ;
	}
	write(x / 10);
	putchar(x % 10 + '0');
}
signed main() {
	n = read(), q = read();
	for (int i = 1; i <= n; i++)
		a[i] = read(), tree.rt[i] = tree.newnode(a[i]);
	for (int i = 2; i <= n; i++)
		f[i] = read(), e[f[i]].push_back(i);
	dfs1(1, 0), dfs2(1, 1);
	tree2.build(1, n, 1);
	for (int i = 1; i <= n; i++)
		if(top[i] == i)
			s.insert(dfn[i]);
	while(q--) {
		int op = read(), x = read();
		if(op == 1)
			write(query(x)), putchar('\n');
		else
			change(x);
	}
	return 0;               
}
posted @ 2025-08-10 20:49  LUlululu1616  阅读(22)  评论(0)    收藏  举报