~~~ 对于一张带权无向联通图 G = ( V , E ) G = (V, E) G=(V,E) 来说,我们定义 f ( u , v ) f(u, v) f(u,v) 表示 u , v u, v u,v 之间的最小割的大小。可以证明存在一颗 ∣ V ∣ |V| ∣V∣ 个节点的树,令 g ( u , v ) g(u, v) g(u,v) 表示树上 u → v u → v u→v 路径上的最小边权,使得 g ( u , v ) = f ( u , v ) g(u, v) = f(u, v) g(u,v)=f(u,v)。换句话说,对于任意的无向图来说,其任意两点之间只会有至多 n − 1 n − 1 n−1种不同的最小割,这 n − 1 n − 1 n−1 种不同的最小割可以构成一个最小割树。
#include
#define ll long long
using namespace std;
const int N=1e3+10;
const int M=2e4+10;
const int inf=1e9;
int n,m,s,t,tot=1,flow,maxflow,ver[M],Next[M],lin[N],edge[M],yedge[M],d[N];
set<int>ge;
struct node{
int id,val;
}a[N];
int read(){
char ch=getchar();int num=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){num=(num<<1)+(num<<3)+(ch^48);ch=getchar();}
return num*f;
}
void add(int x,int y,int z){ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;edge[tot]=z;yedge[tot]=z;}
bool cmp(node a,node b){return d[a.id]<d[b.id];}
bool bfs(){
queue<int>q;
memset(d,0,sizeof(d));
d[s]=1;q.push(s);
while(q.size()){
int x=q.front();q.pop();
for(int i=lin[x];i;i=Next[i]){
int y=ver[i];
if(edge[i]&&!d[y]){
d[y]=d[x]+1;q.push(y);
if(y==t) return 1;
}
}
}return 0;
}
int dinic(int x,int flow){
if(x==t) return flow;
int rest=flow;
for(int i=lin[x];i&&rest;i=Next[i]){
int y=ver[i];
if(d[y]==d[x]+1&&edge[i]){
int k=dinic(y,min(edge[i],rest));
if(!k) d[y]=0;
rest-=k;edge[i]-=k;edge[i^1]+=k;
if(!rest) return flow-rest;
}
}return flow-rest;
}
void CDQ(int l,int r){
if(r==l) return;
s=a[l].id,t=a[r].id;
for(int i=l;i<=r;++i) a[i].val=0;
for(int i=2;i<=tot;++i) edge[i]=yedge[i];
flow=maxflow=0;
while(bfs()){
while(flow=dinic(s,inf)) maxflow+=flow;
}
ge.insert(maxflow);
sort(a+l,a+r+1,cmp);
int mid;
for(int i=l;i<=r;++i) if(d[a[i].id]>0){mid=i-1;break;}
CDQ(l,mid);CDQ(mid+1,r);
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;++i){
int x=read(),y=read(),z=read();
add(x,y,z);add(y,x,z);
}
for(int i=1;i<=n;++i) a[i].id=i;
CDQ(1,n);
printf("%d\n",ge.size());
return 0;
}