洛谷 P3128 [USACO15DEC]最大流Max Flow

本文介绍了一种解决树形结构中两点间路径修改问题的高效算法——树上差分。通过两次DFS实现子树权值的计算,并利用最近公共祖先(LCA)技巧简化路径操作,最终找到权值最大的节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给你一棵树 给你k对点 这两点之间的最短路径上的每个点权值加一 问最后哪个点的权值最大

输入

第一行输入n,k 表示点的个数和修改的个数

接下来的n-1行 每行两个数u,v 表示u,v间有一条边

接下来k行 每行两个数u,v 表示要修改的路径

输出

点权最大的点的权值。



这道题可以用树剖+线段树来做...

但是代码量太大了 果断放弃 

有一种很方便的方法 叫做树上差分

就是每对点 val[u]+1  val[v]+1  设p为u,v 的最近公共祖先

则val[p]-1  val[p的父亲]-1

某个点的点权就是它的子树和

所以模板就是 树剖的两遍dfs 还有求子树和的一遍dfs

最后输出最大的子树和就可以了

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>

using namespace std;

const int N=50000+10;
const int M=100000+10;
const int oo=1e9;

int n,k,head[N],nex[M],tov[M],son[N],dis[N],ans;
int in[N],out[N],seq[N],pre[N],val[N],f[N];
int tot,size[N],idc,top[N]; 

void add(int u,int v){
	tot++;
	nex[tot]=head[u];
	tov[tot]=v;
	head[u]=tot;
}

int max(int a,int b){
	return a<b?b:a;
}

void dfs1(int x,int fa){
	size[x]=1;
	pre[x]=fa;
	for(int i=head[x];i;i=nex[i]){
		int v=tov[i];
		if(v==fa) continue;
		dis[v]=dis[x]+1;
		dfs1(v,x);
		size[x]+=size[v];
		if(!son[x]||size[v]>size[son[x]]) son[x]=v;
	}
}

void dfs2(int x,int tp){
	seq[x]=++idc;
	top[x]=tp;
	in[x]=idc;
	if(son[x]) dfs2(son[x],tp);
	for(int i=head[x];i;i=nex[i]){
		int v=tov[i];
		if(v==pre[x]||v==son[x]) continue;
		dfs2(v,v);
	}
	out[x]=idc;
}

void dfs3(int u){
	f[u]=val[u];
	for(int i=head[u];i;i=nex[i]){
		int v=tov[i];
		if(v==pre[u]) continue;
		dfs3(v);
		f[u]+=f[v];
	}
	ans=max(ans,f[u]);
}

int find_lca(int u,int v){
	while(top[u]!=top[v]){
		if(dis[top[u]]<dis[top[v]]) swap(u,v);
		u=pre[top[u]];
	}
	return dis[u]<dis[v]?u:v;
}

int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	
	int root=1;
	dfs1(root,root);
	dfs2(root,root);
	
	for(int i=1;i<=k;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		val[u]++;
		val[v]++;
		int p=find_lca(u,v);
		val[p]--;
		val[pre[p]]--;
	}
	
	ans=oo;
	ans*=-1;
	dfs3(root);
	printf("%d",ans);
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值