分治最小割树

68 篇文章 0 订阅
23 篇文章 0 订阅

https://blog.csdn.net/oi_Konnyaku/article/details/84927936

https://blog.csdn.net/qq_33229466/article/details/53290996

http://hzwer.com/6248.html

最小割不会互相跨立

所以可以分治。

理论复杂度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);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值