[CQOI2016]不同的最小割

前置芝士:最小割树

    ~~~    对于一张带权无向联通图 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 uv 路径上的最小边权,使得 g ( u , v ) = f ( u , v ) g(u, v) = f(u, v) g(u,v)=f(u,v)。换句话说,对于任意的无向图来说,其任意两点之间只会有至多 n − 1 n − 1 n1种不同的最小割,这 n − 1 n − 1 n1 种不同的最小割可以构成一个最小割树。

(PS:总之记住结论好啦,我也不会证qwq)

[CQOI2016]不同的最小割_第1张图片

  • 求本质不同的最小割有多少种。
  • 因为一张图最多会有 n − 1 n − 1 n1 个不同的最小割,考虑到本题的规模,我们只需要考虑怎么快速的找到所有不同的最小割即可。我们考虑我们只要让每次划分的 S , T S,T S,T 集存在区别,那么就对应了不同的
    最小割。
  • 我们考虑分治来解决这问题,首先对于我们当前分治的点集,我们任选两个点 s , t s,t s,t,然后我们计算这两个点之间的最小割,这个时候整张图会被我们划分为 S , T S,T S,T 两个点集,我们用之前获得的最小割更新所有 a n s ( x , y ) , x ∈ S , y ∈ T ans(x, y), x ∈ S, y ∈ T ans(x,y),xS,yT
  • 这个时候当前对于当前点集划分的两个集合,我们对这两个集合递归的执行刚才的步骤,这个时候我们保证新的 S , T S,T S,T 所划分出的集合一定和原来的集合不同,也就对应了不同的最小割。这样我们就能在一个比较科学的复杂度内计算任意两个点的最小割。
  • 1A 美滋滋
#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;
}

你可能感兴趣的:(网络流)