【題解】SAC E#1 - 一道難題 Tree
阿新 • • 發佈:2020-08-16
\(\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; }