BZOJ4519: [Cqoi2016]不同的最小割
Description
学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成
两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割。对于带权图来说,将
所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在
关于s,t的割中容量最小的割。
而对冲刺NOI竞赛的选手而言,求带权图中两点的最小割已经不是什么难事了。我们可以把
视野放宽,考虑有N个点的无向连通图中所有点对的最小割的容量,共能得到N(N−1)
2个数值。
这些数值中互不相同的有多少个呢?这似乎是个有趣的问题。
Input
输入文件第一行包含两个数N,M,表示点数和边数。接下来M行,每行三个数u,v,w,
表示点u和点v(从1开始标号)之间有条边权值是w。
1<=N<=850 1<=M<=8500 1<=W<=100000
Output
输出文件第一行为一个整数,表示个数。
Sample Input
4 4
1 2 3
1 3 6
2 4 5
3 4 4
1 2 3
1 3 6
2 4 5
3 4 4
Sample Output
3
经典的分治最小割问题,有这样一个结论:最小割最多有n-1个,这n-1个最小割构成一个最小割树(可见2016国家队论文)
大意是每次任取一个源汇求出最小割,然后分治S割和T割的节点,时间复杂度为N*O(最小割)。
#include<cstdio> #include<cctype> #include<queue> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; const int BufferSize=1<<16; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=910; const int maxm=20010; struct Dinic { struct Edge {int from,to,flow;}edges[maxm]; int n,m,s,t,first[maxn],next[maxm]; int d[maxn],vis[maxn],cur[maxn]; void init(int n) { this->n=n;m=0; memset(first,-1,sizeof(first)); } void AddEdge(int u,int v,int w) { edges[m]=(Edge){u,v,w};next[m]=first[u];first[u]=m++; edges[m]=(Edge){v,u,w};next[m]=first[v];first[v]=m++; } void reset() {rep(i,0,m-1) edges[i].flow=edges[i^1].flow=(edges[i].flow+edges[i^1].flow)>>1;} int Q[maxn],clo; int BFS() { int l=1,r=0;Q[++r]=s;vis[s]=++clo; while(l<=r) { int x=Q[l++];cur[x]=first[x]; ren { Edge& e=edges[i]; if(e.flow&&vis[e.to]!=clo) { vis[e.to]=clo; d[e.to]=d[x]+1; Q[++r]=e.to; } } } return vis[t]==clo; } int DFS(int x,int a) { if(x==t||!a) return a; int flow=0,f; for(int& i=cur[x];i!=-1;i=next[i]) { Edge& e=edges[i]; if(d[e.to]==d[x]+1&&(f=DFS(e.to,min(a,e.flow)))) { e.flow-=f;edges[i^1].flow+=f; flow+=f;a-=f;if(!a) break; } } return flow; } int solve(int s,int t) { this->s=s;this->t=t;int flow=0; while(BFS()) flow+=DFS(s,1e9); return flow; } }sol; int cnt,A[maxn],tmp[maxn],ans[maxm]; void solve(int l,int r) { if(l>=r) return;sol.reset(); ans[++cnt]=sol.solve(A[l],A[r]); int L=l,R=r; rep(i,l,r) { if(sol.vis[A[i]]==sol.clo) tmp[L++]=A[i]; else tmp[R--]=A[i]; } rep(i,l,r) A[i]=tmp[i]; solve(l,R);solve(L,r); } int main() { int n=read(),m=read();sol.init(n); rep(i,1,m) { int a=read(),b=read(),c=read(); sol.AddEdge(a,b,c); } rep(i,1,n) A[i]=i;solve(1,n); sort(ans+1,ans+cnt+1); int res=1; rep(i,2,cnt) if(ans[i]!=ans[i-1]) res++; printf("%d\n",res); return 0; }