1. 程式人生 > 實用技巧 >【題解】SAC E#1 - 一道難題 Tree

【題解】SAC E#1 - 一道難題 Tree

Problem is here

\(\text{Solution:}\)

首先,一眼看出這是最小割,只要葉子節點對匯點\(T\)連線流量為\(inf\)的邊就可以一遍最大流搞定了。

剩下的問題在於,如何判斷邊的方向。

可以用\(dfs\)實現,方向由源點\(S\to T.\)而邊權,注意到我們連的邊是雙向邊,且編號連續。利用這一點我們可以在\(dfs\)裡面對邊進行賦值。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
const int inf=(1<<30);
struct edge{
	int nxt,to,flow;
}e[MAXN];
int n,head[MAXN],S,T,tot=1;
int dep[MAXN],cur[MAXN],v[MAXN];
bitset<MAXN>vis;
vector<int>leaf;
inline void add(int x,int y,int w){
	e[++tot].to=y;e[tot].nxt=head[x];e[tot].flow=w;head[x]=tot;
	e[++tot].to=x;e[tot].nxt=head[y];e[tot].flow=0;head[y]=tot;
}
bool bfs(int s,int t){
	memset(dep,0,sizeof(dep));
	dep[s]=1;cur[s]=head[s];
	queue<int>q;q.push(s);
	while(!q.empty()){
		s=q.front();q.pop();
		for(int i=head[s];i;i=e[i].nxt){
			int j=e[i].to;
			if(!dep[j]&&e[i].flow){
				dep[j]=dep[s]+1;
				cur[j]=head[j];
				if(j==t)return true;
				q.push(j);
			}
		}
	}
	return false;
}
int dfs(int s,int flow,int t){
	if(s==t||flow<=0)return flow;
	int rest=flow;
	for(int i=cur[s];i;i=e[i].nxt){
		int j=e[i].to;
		if(e[i].flow&&dep[j]==dep[s]+1){
			int tmp=dfs(j,min(rest,e[i].flow),t);
			if(tmp<=0)dep[j]=0;
			rest-=tmp;e[i].flow-=tmp;e[i^1].flow+=tmp;
			if(rest<=0)break;
		}
	}
	return flow-rest;
}
int dinic(int s,int t){
	int ans=0;
	while(bfs(s,t))ans+=dfs(s,inf,t);
	return ans;
}
void dfs1(int x){
	vis[x]=1;int fg=0;
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].to;
		if(vis[j])continue;
		fg=1;dfs1(j);
		e[i].flow=v[i|1];
	}
	if(!fg)leaf.push_back(x);
}
int main(){
	scanf("%d%d",&n,&S);T=n+1;
	for(int i=1;i<n;++i){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,0);add(b,a,0);v[tot]=c;
	}
	dfs1(S);
	for(int i=0;i<(int)leaf.size();++i)add(leaf[i],T,inf);
	printf("%d\n",dinic(S,T));
	return 0;
}