CF2206D Christmas Tree Un-decoration Sol

题目链接

我还是不太擅长这种节点可能记录无用信息的线段树。

DP

先想想 \(O(n^2)\) 单次的DP。定义 \(f_u\) 为处理完以 \(u\) 为根的子树所需要的答案。那么显然答案为 \(\max(\sum\limits_{v\in u} f_v,a_u)\)

但是我们看着数据范围,肯定想尝试上树剖进行维护,定义 \(b_u\) 表示 \(u\) 节点所有轻儿子\(dp\) 之和。则答案变成 \(max(b_u+dp_{son_v},a_u)\)

线段树维护

假设现在存在一条链 \(c_1,c_2,c_3,c_4,\dots,c_{len}\),则 \(dp_{c_1} = \max(a_{c_1},b_{c_1} + dp_{c_2})\),展开,有 \(dp_{c_1} = \max(a_{c_1},b_{c_1} + b_{c_2} + dp_{c_3},b_{c_1}+a_{c_2})\)。不难发现答案实际上为 \(\max\limits_{i=1}^{len}(a_i + \sum\limits_{j=1}^{i-1} b_j)\)

因此我们尝试使用线段树和树剖进行维护,让线段树的每一个节点记录 \(s\)\(mx\) 代表着 \(b\) 和这段区间内答案的最大值,需要注意的是,这里的答案并不考虑不在这段区间内的下面的链,这是需要我们在查询时查询一段完整的链实现的。具体的,可以看代码。

当然,可能会有疑问,线段树上的节点不是可能记录错的信息吗?实际上我们不会用到错误的信息的,在每次查询是,我们将范围严格限制在一条链上,因此不会访问到错误的信息。

时间复杂度 \(O(n \log^2 n )\)

Code

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 2e5 + 10;
vector<int> G[N];
int n,Q;
int a[N];
int fa[N];
int siz[N],son[N];
int dfn[N],idfn[N],top[N],ed[N],tot;
void dfs1(int u){
	siz[u] = 1;
	for(int v : G[u]){
		dfs1(v);
		siz[u] += siz[v];
		if(siz[son[u]] < siz[v])
			son[u] = v;
	}
	return ;
}
LL b[N],dp[N];
void dfs2(int u,int tp){
	dfn[u] = ++tot;idfn[tot] = u;top[u] = tp;ed[tp] = u;
	if(son[u])
		dfs2(son[u],tp);
	for(int v : G[u]){
		if(v == son[u]) continue;
		dfs2(v,v);
		b[u] += dp[v];
	}
	if(son[u])
		dp[u] = max(1ll * a[u],dp[son[u]]+b[u]);
	else
		dp[u] = a[u];
	return ;
}
struct node{
	LL s,mx;
};
node merge(node x,node y){
	return {x.s+y.s,max(x.mx,x.s+y.mx)};
}
struct Segment_Tree{
	node tre[N<<2];
	void build(int pl,int pr,int p){
		if(pl == pr){
			tre[p] = {b[idfn[pl]],a[idfn[pl]]};
			tre[p].mx = max(tre[p].s,tre[p].mx);//注意:这里不能将mx设置为 dp_u,因为真正要得到 dp_u 需要通过链上的合并得到。
			return ;
		}
		int mid = pl + pr >> 1;
		build(pl,mid,p*2);
		build(mid+1,pr,p*2+1);
		tre[p] = merge(tre[p*2],tre[p*2+1]);
		return ;
	}
	void clear(int pl,int pr,int p){
		if(pl > pr) return ;
		tre[p] = {0,0};
		if(pl == pr)
			return ;
		int mid = pl + pr >> 1;
		clear(pl,mid,p*2);
		clear(mid+1,pr,p*2+1);
		return ;
	}
	void update(int x,int pl,int pr,int p,node t){
		if(pl == pr){
			if(t.s != -1) tre[p].s = t.s;
			if(t.mx != -1) tre[p].mx = t.mx;
			tre[p].mx = max(tre[p].s,tre[p].mx);
			return ;
		}
		int mid = pl + pr >> 1;
		if(x <= mid) update(x,pl,mid,p*2,t);
		else update(x,mid+1,pr,p*2+1,t);
		tre[p] = merge(tre[p*2],tre[p*2+1]);
		return ;
	}
	node query(int L,int R,int pl,int pr,int p){
		if(L <= pl && pr <= R) return tre[p];
		int mid = pl + pr >> 1;
		if(L <= mid && mid < R)
			return merge(query(L,R,pl,mid,p*2),query(L,R,mid+1,pr,p*2+1));
		if(L <= mid)
			return query(L,R,pl,mid,p*2);
		return query(L,R,mid+1,pr,p*2+1);
	}
}sgt;
void solve(){
	tot = 0;
	for(int i=1;i<=n;i++){
		G[i].clear();
		dp[i] = fa[i] = b[i] = 0;
		top[i] = dfn[i] = ed[i] = idfn[i] = 0;
		son[i] = 0;
	}
	sgt.clear(1,n,1);
	cin >> n >> Q;
	for(int i=2;i<=n;i++){
		int x;cin >> x;
		G[x].push_back(i);
		fa[i] = x;
	}
	for(int i=1;i<=n;i++)
		cin >> a[i];
	dfs1(1);
	dfs2(1,1);
	sgt.build(1,n,1);
	cout << sgt.query(dfn[top[1]],dfn[ed[1]],1,n,1).mx << "\n";
	while(Q -- ){
		int u,x;
		cin >> u >> x;
		a[u] = x;
		LL pre = sgt.query(dfn[top[u]],dfn[ed[top[u]]],1,n,1).mx;
		sgt.update(dfn[u],1,n,1,{-1,x});
		LL nw = sgt.query(dfn[top[u]], dfn[ed[top[u]]], 1, n, 1).mx;
		u = fa[top[u]];
		while(u){
			node sum = sgt.query(dfn[u],dfn[u],1,n,1);
			LL pree = sgt.query(dfn[top[u]],dfn[ed[top[u]]],1,n,1).mx;
			sgt.update(dfn[u],1,n,1,{sum.s-pre+nw,a[u]});
			pre = pree;
			nw = sgt.query(dfn[top[u]],dfn[ed[top[u]]],1,n,1).mx;
			u = fa[top[u]];
		}
		cout << sgt.query(dfn[top[1]],dfn[ed[1]],1,n,1).mx << "\n";
	}
}
int main(){
	IOS;
	// File("tree");
	int T;
	cin >> T;
	while(T -- )
		solve();
	return 0;
}
posted @ 2026-05-20 07:47  WinterXorSnow  阅读(9)  评论(0)    收藏  举报