https://blog.csdn.net/oi_Konnyaku/article/details/84927936
https://blog.csdn.net/qq_33229466/article/details/53290996
最小割不会互相跨立
所以可以分治。
理论复杂度O(n^4),
Dinic写得好可以看做O(n^2logn)( 然而楼主写不好 )
code:
//分治最小割树 O(n^4)
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<set>
#define maxn 855
#define maxm 8505*2
#define inf 0x3f3f3f3f
using namespace std;
set<int>st;
int n,m;
int ret[maxn],cnt_ret;
int info[maxn],Prev[maxm],to[maxm],cap[maxm],cnt_e=1;
inline void Node(int u,int v,int c)
{
Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v;
cap[cnt_e]=c;
}
inline void Line(int u,int v,int c)
{
Node(u,v,c),Node(v,u,c);
}
namespace Dinic
{
int dis[maxn],stm,flag,c[maxn],tmp[maxn];
int Q[maxn],L,R,s,t;
inline bool BFS()
{
memset(dis,0,sizeof dis);
L=R=0;
dis[Q[R++]=t]=0;
for(int now;L<R;)
{
now=Q[L++];
for(int i=info[now];i;i=Prev[i])
if(cap[i^1] && !dis[to[i]] && to[i]!=t)
dis[Q[R++]=to[i]]=dis[now]+1;
}
return dis[s]>0;
}
inline int aug(int now,int Max)
{
if(now==t)
return Max;
int inc,st=Max;
for(int i=info[now];i && st;i=Prev[i])
if(cap[i] && dis[to[i]]+1==dis[now])
{
inc=aug(to[i],min(st,cap[i]));
cap[i]-=inc,cap[i^1]+=inc,st-=inc;
}
return Max-st;
}
inline int sap(int S,int T)
{
s=S,t=T;
for(stm=0;BFS();)
stm+=aug(S,inf);
return stm;
}
inline void Solve(int L,int R)
{
if(L>=R) return;
for(int i=2;i<=cnt_e;i+=2) cap[i]=cap[i^1]=(cap[i]+cap[i^1])>>1;
int cut=sap(c[L],c[R]),l=L,r=R;
for(int i=L;i<=R;i++)
tmp[dis[c[i]] || c[i]==t ? l++:r-- ]=c[i];
for(int i=L;i<=R;i++)
c[i]=tmp[i];
st.insert(cut);
Solve(L,r),Solve(l,R);
}
}
char ch;
inline void get(int &res)
{
for(;!isdigit(ch=getchar()););
for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');
}
int main()
{
int u,v,w;
get(n),get(m);
for(int i=1;i<=m;i++)
{
get(u),get(v),get(w);
Line(u,v,w);
}
for(int i=1;i<=n;i++) Dinic::c[i]=i;
Dinic::Solve(1,n);
printf("%d\n",st.size());
}
20191204UPD:
sap超短。
#include<bits/stdc++.h>
#define maxn 855
#define maxm 20005
#define inf 0x3f3f3f3f
#define Clear(a,b) memset(a,b,sizeof a)
#define Copy(a,b) memcpy(a,b,sizeof a)
using namespace std;
int n,m,c[maxn];
int info[maxn],Prev[maxm],to[maxm],cap[maxm],cnt_e=1;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c; }
void Line(int u,int v,int c,int d=0){ Node(u,v,c),Node(v,u,d); }
int sb[maxn];
int S,T,h[maxn],g[maxn],buf[maxn],vis[maxn],rc[maxn];
int aug(int u,int mx){
if(u == T) return mx;
int st = mx , inc;
for(int i=info[u],v;i;i=Prev[i])
if(cap[i] && h[v=to[i]]+1 == h[u] && (inc = aug(v,min(st,cap[i])))){
cap[i] -= inc , cap[i^1] += inc;
if(!(st-=inc) || h[0]) return mx-st;
}
if(!--g[h[u]]) h[0]=1;
++g[++h[u]],info[u]=buf[u];
return mx-st;
}
void dfs(int u){ vis[u]=1;for(int i=info[u],v;i;i=Prev[i]) if(cap[i]&&!vis[v=to[i]]) dfs(v); }
void Solve(int L,int R){
if(L>=R) return;
int stm=0,l=L,r=R;
for(S=c[L],T=c[R],Clear(h,0),Clear(g,0),Clear(vis,0);!h[0];) stm+=aug(S,inf);
Copy(info,buf),sb[++sb[0]]=stm,dfs(S);
for(int i=2;i<=cnt_e;i+=2) cap[i] = cap[i^1] = (cap[i] + cap[i^1]) / 2;
for(int i=L;i<=R;i++) rc[vis[c[i]]?l++:r--]=c[i];
for(int i=L;i<=R;i++) c[i]=rc[i];
Solve(L,l-1),Solve(r+1,R);
}
int main(){
scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) c[i]=i;
for(int i=1,u,v,w;i<=m;i++) scanf("%d%d%d",&u,&v,&w),Line(u,v,w,w);
Copy(buf,info),Solve(1,n),sort(sb+1,sb+1+sb[0]);
printf("%d\n",unique(sb+1,sb+1+sb[0])-sb-1);
}
20190315 UPD:
原来最小割树真的是一颗树。。
LG P4897 【模板】最小割树(Gomory-Hu Tree
首先有一个定理,就是一个n个点的图上,两点之间只有n中本质不同的最小割。因此一定存在一棵树,满足树上两点的最小割等于原图上两点的最小割。我们把这样的树称之为“等价流树”。建立最小割树的常用算法就是Gusfield算法
但是!Gomory-Hu树并不是等价流树。它的每条边需要多满足一个删掉这边后的两个树所构成的两个点集间的最小割等于这条边的边权的性质。
所以建树不是一颗二叉树。。。get到了正确的建树姿势。
AC Code:
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define maxn 1505
#define maxm 3005
#define lim 10
#define inf 0x3f3f3f3f
using namespace std;
int n,m,c[maxn],rc[maxn];
struct edge{ int u,v,w; }e[maxm];
int f[lim][maxn],g[lim][maxn],dep[maxn];
int info[maxn],Prev[maxm],to[maxm],cap[maxm],cnt_e=1;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c; }
void Line(int u,int v,int c,int d=0){ Node(u,v,c),Node(v,u,d); }
int S,T,h[maxn];
int aug(int now,int Max){
if(now == T) return Max;
int inc , st = Max;
for(int i=info[now];i;i=Prev[i])if(cap[i]&&h[to[i]]+1==h[now]){
inc = aug(to[i],min(cap[i],st));
st -= inc , cap[i] -= inc , cap[i^1] += inc;
if(!st) break;
}
return Max - st;
}
bool BFS()
{ static int q[maxn],L,R;
memset(h,-1,sizeof h);
h[q[L=R=0]=T]=0,R++;
for(int now;L<R;){
now = q[L++];
for(int i=info[now];i;i=Prev[i])
if(cap[i^1]&&h[to[i]]==-1)
h[q[R++]=to[i]]=h[now]+1;
}
return h[S] != -1;
}
vector<int>G[maxn],C[maxn];
void Solve(int L,int R)
{
if(L>=R) return;
memset(info,0,sizeof info),cnt_e=1;
for(int i=1;i<=m;i++) Line(e[i].u,e[i].v,e[i].w,e[i].w);
int stm = 0;
for(S=c[L],T=c[R];BFS();) stm += aug(S,inf);
G[S].push_back(T),C[S].push_back(stm);
G[T].push_back(S),C[T].push_back(stm);
int l = L , r = R;
for(int i=L;i<=R;i++) rc[h[c[i]]==-1?l++:r--] = c[i];
for(int i=L;i<=R;i++) c[i] = rc[i];
Solve(L,r),Solve(l,R);
}
void dfs(int now,int ff)
{
dep[now] = dep[f[0][now] = ff] + 1;
for(int i=0,siz=G[now].size(),v;i<siz;i++)
if((v=G[now][i])!=ff)
dfs(v,now),g[0][v] = C[now][i];
}
int main()
{
scanf("%d%d",&n,&m),n++;
for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w),e[i].u++,e[i].v++,c[i]=i;
Solve(1,n);
dfs(1,0);
for(int j=1;j<lim;j++)
for(int i=1;i<=n;i++)
f[j][i] = f[j-1][f[j-1][i]],
g[j][i] = min(g[j-1][i] , g[j-1][f[j-1][i]]);
int q;
scanf("%d",&q);
for(;q--;)
{
int u,v;
scanf("%d%d",&u,&v) , u++ , v++;
int ret = 0x3f3f3f3f;
if(dep[u] < dep[v]) swap(u,v);
for(int i=0;dep[u]!=dep[v];i++)
if((dep[u]-dep[v])>>i&1)
ret=min(ret,g[i][u]),u=f[i][u];
if(u!=v)
{
for(int i=lim-1;i>=0;i--)
if(f[i][u]!=f[i][v])
ret=min(ret,min(g[i][u],g[i][v])),
u=f[i][u],v=f[i][v];
ret=min(ret,min(g[0][u],g[0][v]));
}
printf("%d\n",ret);
}
}