• 最小割树学习笔记


    定义:定义一棵树 $T$ 为最小割树,如果对于树上的所有边 $(u,v)$,树上去掉 $(u,v)$ 后产生的两个集合恰好是原图上 $(u,v)$ 的最小割把原图分成的两个集合,且边 $(u,v)$ 的权值等于原图上 $(u,v)$ 的最小割

    最小割树性质:图中 $(s,t)$ 的最小割等于最小割树上 $s$ 到 $t$ 的路径上最小的边权

    按照定义构造即可 $O(n^3m)$

    #include<cstdio>
    
    #define maxn 555
    #define maxm 2222
    #define INF 1000000000
    
    inline int read(){
    	int r=0,f=1;
    	char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9')r=(r<<1)+(r<<3)+(c^48),c=getchar();
    	return r*f;
    }
    
    inline int min(int a,int b){
    	return a<b?a:b;
    }
    
    inline void swap(int &a,int &b){
    	int c=a;
    	a=b;
    	b=c;
    }
    
    int n,m,q;
    
    struct Flow{
    
    	struct E{
    		int v,c,nxt;
    		E() {}
    		E(int v,int c,int nxt):v(v),c(c),nxt(nxt) {}
    	}e[maxm*4];
    
    	int s,t,hd,tl,s_e,head[maxn],cur[maxn],lev[maxn],q[maxn];
    
    	inline void a_e(int u,int v,int c){
    		e[++s_e]=E(v,c,head[u]);
    		head[u]=s_e;
    	}
    
    	inline void add(int u,int v,int c){
    		a_e(u,v,c);
    		a_e(v,u,0);
    	}
    
    	inline void init(int S,int T){
    		s_e=1;
    		s=S,t=T;
    		for(int i=1;i<=n;i++)head[i]=0;
    	}
    
    	inline bool BFS(){
    		hd=tl=0;
    		for(int i=1;i<=n;i++)
    			lev[i]=0,cur[i]=head[i];
    		lev[s]=1;
    		q[++tl]=s;
    		while(hd^tl){
    			int u=q[++hd];
    			for(int i=head[u];i;i=e[i].nxt){
    				int v=e[i].v,c=e[i].c;
    				if(!c||lev[v])continue;
    				lev[v]=lev[u]+1;
    				q[++tl]=v;
    				if(v==t)return true;
    			}
    		}
    		return false;
    	}
    
    	int dfs(int u,int f){
    		int d=0,used=0;
    		if(!(u^t))return f;
    		for(int &i=cur[u];i;i=e[i].nxt){
    			int v=e[i].v;
    			if(!e[i].c||(lev[v]^(lev[u]+1)))continue;
    			d=dfs(v,min(f-used,e[i].c));
    			if(!d)continue;
    			used+=d;
    			e[i].c-=d;
    			e[i^1].c+=d;
    			if(used==f)break;
    		}
    		if(!used)lev[u]=0;
    		return used;
    	}
    
    	inline int dinic(){
    		int max_flow=0,d=0;
    		while(BFS())
    			while((d=dfs(s,INF)))
    				max_flow+=d;
    		return max_flow;
    	}
    
    }flow;
    
    struct Tree{
    
    	struct E{
    		int v,w,nxt;
    		E() {}
    		E(int v,int w,int nxt):v(v),w(w),nxt(nxt) {}
    	}e[maxm*2];
    
    	int s_e,head[maxn],dot[maxn],be[maxn],uu[maxm],vv[maxm],cc[maxm];
    
    	bool vis[maxn];
    
    	inline void a_e(int u,int v,int w){
    		e[++s_e]=E(v,w,head[u]);
    		head[u]=s_e;
    	}
    
    	inline void init(){
    		s_e=0;
    		for(int i=1;i<=n;i++)
    			dot[i]=be[i]=i,head[i]=0;
    		for(int i=1;i<=m;i++)
    			uu[i]=read(),vv[i]=read(),cc[i]=read();
    	}
    
    	inline void add(){
    		for(int i=1;i<=m;i++){
    			flow.add(uu[i],vv[i],cc[i]);
    			flow.add(vv[i],uu[i],cc[i]);
    		}
    	}
    
    	inline int val(int s,int t){
    		flow.init(s,t),add();//每次都要重置
    		return flow.dinic();
    	}
    
    	inline void find(int l,int r){
    		for(int i=1;i<=flow.tl;i++){
    			int u=flow.q[i];
    			if(be[u]>=l&&be[u]<=r)vis[u]=1;
    		}
    	}
    
    	void build(int l,int r){
    		if(l>=r)return;
    		int ans=val(dot[l],dot[r]);
    		a_e(dot[l],dot[r],ans);
    		a_e(dot[r],dot[l],ans);
    		find(l,r);
    		int i=l,j=r,mid=l-1;
    		while(i<j){
    			while(vis[dot[i]])i++;//dinic里最后一次BFS因为没有增广路了,所以遍历到的就是割完了以后的集合
    			while(!vis[dot[j]])j--;
    			if(i>j)break;
    			swap(be[dot[i]],be[dot[j]]);
    			swap(dot[i],dot[j]);
    			i++,j--;
    		}
    		for(int i=l;i<=r;i++)
    			mid+=vis[dot[i]],vis[dot[i]]=0;
    		build(l,mid);
    		build(mid+1,r);
    	}
    
    }tree;
    
    struct LCA{
    	
    	int er[22],lg[maxn],dep[maxn],anc[22][maxn],dp[22][maxn];
    
    	void dfs(int u,int fa){
    		anc[0][u]=fa;
    		dep[u]=dep[fa]+1;
    		for(int i=tree.head[u];i;i=tree.e[i].nxt){
    			int v=tree.e[i].v,w=tree.e[i].w;
    			if(v==fa)continue;
    			dp[0][v]=w;
    			dfs(v,u);
    		}
    	}
    
    	inline void init(){
    		dfs(1,1);
    		er[0]=1,lg[0]=-1,dp[0][1]=INF;
    		for(int i=1;i<=n;i++)lg[i]=lg[(i>>1)]+1;
    		for(int i=1;i<=lg[n];i++)er[i]=er[(i-1)]<<1;
    		for(int i=1;i<=lg[n];i++)
    			for(int j=1;j<=n;j++){
    				anc[i][j]=anc[i-1][anc[i-1][j]];
    				dp[i][j]=min(dp[i-1][j],dp[i-1][anc[i-1][j]]);
    			}
    	}
    
    	inline int ans(int u,int v){
    		int Min=INF;
    		if(dep[u]>dep[v])swap(u,v);
    		int c=dep[v]-dep[u];
    		while(c){
    			Min=min(Min,dp[lg[c]][v]);
    			v=anc[lg[c]][v];
    			c-=er[lg[c]];
    		}
    		if(u==v)return Min;
    		for(int i=lg[dep[u]];i>=0;i--){
    			if(anc[i][u]==anc[i][v])continue;
    			Min=min(Min,min(dp[i][u],dp[i][v]));
    			u=anc[i][u],v=anc[i][v];
    		}
    		return min(Min,min(dp[0][u],dp[0][v]));
    	}
    
    }lca;
    
    int main(){
    	n=read(),m=read();
    	tree.init();
    	tree.build(1,n);
        q=read();
    	lca.init();
    	for(int i=1;i<=q;i++){
    		int x=read(),y=read();
    		printf("%d\n",lca.ans(x,y));
      	}
    	return 0;
    }
    

      

  • 相关阅读:
    SharePoint SSS(Security Store Service)服务-PowerShell
    SharePoint BDC(Business Data Connectivity)服务-PowerShell
    win32编辑控件字体
    创建选项卡控件
    利用VkKeyScanA判断大写字母
    使用powershell的remove
    x86和x64下指针的大小
    不使用C库函数(Sprintf)将void* 指针转换为十六进制字符串
    使用pycharm,配置环境
    使用python获得屏幕截图并保存为位图文件
  • 原文地址:https://www.cnblogs.com/wyzwyz/p/12204122.html
Copyright © 2020-2023  润新知