二次扫描与换根法 acwng 286 积蓄程度

题意 :有一个树形的水系,由N-1条河道和N个交叉点组成。每条河道有个容量

简单来讲就是有课树形湖泊,度数为1的为汇点,现在让你找到一个源点,求从源点流向汇点水系流量最大。

题解:暴力的做法 就是枚举源点然后  求水系的流量时间复杂度为o(n*n)

但如果用二次扫描与换根法即可将时间复杂度降为o(n);

二次扫描与换根法

1 第一次扫描 任选一个点为根,对整颗树执行一树形dp ,自低向上的状态转移 

2 第二次扫描 从刚选的根,对整颗树执行一次深度优先遍历,每次实现自顶向下的推导,从而得到答案 

对于本题我们设f[x]为表示把x作为源点,流向整个水系,流量最大是多少;

设d[x]为第一次扫描 以root为根,从x出发流向子树的流量

对于根节点 root 有f[root]=d[root]

对与x节点到他的子节点y来讲 如果 x和y的度数都大于一。则f[y]首先有d[y]构成

剩下的部分是由 从y流向x,和x流向其他子节点组成

y流向x则是c(x,y)(x与y边的容量)

x流向其他节点 则是f[x]-min(d[y],c(x,y)) (如果这个不理解可以自己画图看看)

同理对与x的度数等于一

则f[y]=d[y]+c(x,y);

对于y的度数等于一

f[y]=d[y]+min(f[x]-c(x,y),c(x,y));

# include <bits/stdc++.h>
using namespace std;
int t;
int n;
const int N=1*1e6;
int edge[N],ver[N],Next[N],head[N];
int cnt=0;
int f[N];
void add(int a,int b,int c) {
	ver[++cnt]=b;
	edge[cnt]=c;
	Next[cnt]=head[a];
	head[a]=cnt;
}
int fa[N];
int d[N];
int v[N];
void dp(int x) {
	v[x]=1;
	d[x]=0;
	for(int i=head[x]; i; i=Next[i]) {
		int y=ver[i];
		if(v[y]) {
			continue ;
		}
		dp(y);
		if(fa[y]==1) {
			d[x]+=edge[i];
		} else {
			d[x]+=min(d[y],edge[i]);
		}
	}
	return ;
}
void dfs(int x) {
	v[x]=1;
	for(int i=head[x]; i; i=Next[i]) {
		int y=ver[i];
		if(v[y]) {
			continue ;
		}
		if(fa[x]==1) {
			f[y]=d[y]+edge[i];
		} else if(fa[y]==1) {
			f[y]=d[y]+min(f[x]-edge[i],edge[i]);
		} else {
			f[y]=d[y]+min(f[x]-min(d[y],edge[i]),edge[i]);
		}
		dfs(y);
	}
	return ;

}
int main() {
	int t;
	cin>>t;
	while(t--) {
		memset(head,0,sizeof(head));
		memset(f,0,sizeof(f));
		memset(d,0,sizeof(d));
		memset(fa,0,sizeof(fa));
		memset(v,0,sizeof(v));
		cnt=0;
		cin>>n;
		for(int i=1; i<=n-1; i++) {
			int a,b,c;
			cin>>a>>b>>c;
			fa[a]++;
			fa[b]++;
			add(a,b,c);
			add(b,a,c);
		}
		int root=1;
		dp(root);
		memset(v,0,sizeof(v));
		f[root]=d[root];
		dfs(root);
		int ans=0;
        for(int i=1;i<=n;i++)
        {
        	  ans=max(ans,f[i]);
		}
		cout<<ans<<endl;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值