升!龙!

题目链接:https://ac.nowcoder.com/acm/contest/106318/G

题意:

给定一颗子树,规定其val为 从根节点1到叶子节点的简单路径的权值之和 的max
现在给出q次查询,每次查询将x节点连接到y节点之下,求此时的val(每次操作不改变原树)

思路:

DFS序(树形dp)+查询最大值

发现每次移动都只会改变x节点及其子树的路径权值和(从x节点的father变为y节点)
所以我们要在答案中刨去原子树 x节点及其子树的答案 ,并与更新后的 x节点及其子树的答案 取max即可

为了实现要求,需要在DFS进行树形DP
我们需要记录:
w[i]:从根节点1到i节点的路径权值
f[i]:从节点i到其子树的 路径权值和 的最大值
那么移动后 x节点及其子树的答案 即为w[y]+f[x]

我们也需要知道原先刨去 x节点及其子树的答案 的答案
通过DFS序将每个节点映射到一维数组
记录lev[i]:节点i 刚好搜索完子树 此时的节点
所以 节点i及其子树 在一维数组中代表的区间即为:[i,lev[i]]
那么[1,i-1],[lev[i]+1,n]即为刨去后的区间
显然答案就是两个区间w[i]的最大值
我们需要快速查询两个区间,可以用线段树维护,也可ST表,也可 前缀最大值+后缀最大值

vector<int>e[maxn];
int f[maxn];
int w[maxn];
int a[maxn];
int n;
map<int,int>mp;
int cnt;
int lev[maxn];
void dfs(int u,int fa,int now){
	
	w[u]=now+a[u];
	mp[u]=++cnt;
	int res=a[u];
	
	for(int v:e[u]){
		if(v==fa)continue;
		dfs(v,u,w[u]);
		res=max(res,f[v]+a[u]);
	}
	
	lev[u]=cnt;
	f[u]=res;
}

int pre[maxn];
int suf[maxn];
int dfsn[maxn];
void solve(){
	int q;cin>>n>>q;

	rep(i,1,n)cin>>a[i];
	for(int i=2;i<=n;i++){
		int p;cin>>p;
		e[p].pb(i);e[i].pb(p);
	}
	dfs(1,0,0);
	for(auto x:mp){
		dfsn[x.se]=x.fi;
	}

	rep(i,1,n)pre[i]=max(pre[i-1],w[dfsn[i]]);
	for(int i=n;i>=1;i--)suf[i]=max(suf[i+1],w[dfsn[i]]);
		
	rep(i,1,q){
		int x,y;cin>>x>>y;
		
		int l=mp[x],r=lev[x];
		
		int ans=0;
		ans=max(ans,f[x]+w[y]);
		ans=max(ans,max(pre[l-1],suf[r+1]));

		cout<<ans<<endl;
	}
	
}
posted @ 2025-04-07 16:48  Marinaco  阅读(18)  评论(0)    收藏  举报
//雪花飘落效果