zoukankan      html  css  js  c++  java
  • 【bzoj2229】 Zjoi2011—最小割

    http://www.lydsy.com/JudgeOnline/problem.php?id=2229 (题目链接)

    题意

      给出一张无向图,$q$组询问,每次询问最小割不大于$c$的点对数量。

    Solution

      orz:DaD3zZ

      最小割树什么的好神,但是看不懂啊,不如直接撸代码= =。根据网上神犇的理论,貌似最小割的数目不会超过$n-1$个,所以可以将它构成一棵最小割树。

      不过我们的实现并不需要考虑怎么构树。直接暴力的话就是枚举点对,要做$n^2$次$Dinic$,我们通过选择一些优秀的点对来减少$Dinic$的次数。每次分治,任选两个在当前分治区间中的点作为源点和汇点,在原图上做一次$Dinic$,将原图分为了两个割集$S$和$T$,更新$S$和$T$之间的点的最小割。将这两个割集与分治区间取交得到分值区间的割集$S'$和$T'$,然后递归处理$S'$和$T'$就可以了。

      值得注意的是,这样子并没有减小问题的规模,只是通过有技巧的选择源点和汇点来减少$Dinic$的次数(虽然我也不知道为什么这样是正确的)。复杂度大概是$O(kn*Dinic)$,$k$这个常数应该不会太大,出题人总不会丧心病狂卡这玩意儿吧,大不了random_shuffle一下= =。

    细节

      无向图。

    代码

    // bzoj2229
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #define LL long long
    #define inf (1ll<<30)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
    using namespace std;
    
    const int maxn=200,maxm=10010;
    int Q,n,m,cnt,id[maxn],head[maxn],ans[maxn][maxn],vis[maxn],tmp[maxn];
    struct edge {int to,next,w;}e[maxm];
    
    namespace Dinic {
    	int d[maxn],S,T;
    	void link(int u,int v,int w) {
    		e[++cnt]=(edge){v,head[u],w};head[u]=cnt;
    		e[++cnt]=(edge){u,head[v],w};head[v]=cnt;
    	}
    	bool bfs() {
    		memset(d,-1,sizeof(d));
    		queue<int> q;q.push(S);d[S]=0;
    		while (!q.empty()) {
    			int x=q.front();q.pop();
    			for (int i=head[x];i;i=e[i].next)
    				if (e[i].w && d[e[i].to]<0) d[e[i].to]=d[x]+1,q.push(e[i].to);
    		}
    		return d[T]>0;
    	}
    	int dfs(int x,int f) {
    		if (x==T || f==0) return f;
    		int w,used=0;
    		for (int i=head[x];i;i=e[i].next) if (e[i].w && d[e[i].to]==d[x]+1) {
    				w=dfs(e[i].to,min(e[i].w,f-used));
    				used+=w,e[i].w-=w,e[i^1].w+=w;
    				if (used==f) return used;
    			}
    		if (!used) d[x]=-1;
    		return used;
    	}
    	int main(int x,int y){
    		S=x,T=y;int flow=0;
    		while (bfs()) flow+=dfs(S,inf);
    		return flow;
    	}
    }
    using namespace Dinic;
    
    void Init() {
    	cnt=1;
    	memset(head,0,sizeof(head));
    	memset(ans,0x7f,sizeof(ans));
    }
    void dfs(int x) {
    	vis[x]=1;
    	for (int i=head[x];i;i=e[i].next)
    		if (e[i].w && !vis[e[i].to]) dfs(e[i].to);
    }
    void solve(int L,int R) {
    	if (L==R) return;
    	for (int i=2;i<=cnt;i+=2) e[i].w=e[i^1].w=(e[i].w+e[i^1].w)>>1;
    	int flow=Dinic::main(id[L],id[R]);
    	memset(vis,0,sizeof(vis));dfs(id[L]);
    	for (int i=1;i<=n;i++) {
    		if (!vis[i]) continue;
    		for (int j=1;j<=n;j++)
    			if (!vis[j]) ans[i][j]=ans[j][i]=min(ans[i][j],flow);
    	}
    	int l=L,r=R;
    	for (int i=L;i<=R;i++) vis[id[i]] ? tmp[l++]=id[i] : tmp[r--]=id[i];
    	for (int i=L;i<=R;i++) id[i]=tmp[i];
    	solve(L,l-1);solve(r+1,R);
    }
    
    int main() {
    	int T;scanf("%d",&T);
    	while (T--) {
    		scanf("%d%d",&n,&m);Init();
    		for (int i=1;i<=n;i++) id[i]=i;
    		for (int u,v,w,i=1;i<=m;i++) {
    			scanf("%d%d%d",&u,&v,&w);
    			Dinic::link(u,v,w);
    		}
    		solve(1,n);
    		scanf("%d",&Q);
    		for (int c,i=1;i<=Q;i++) {
    			scanf("%d",&c);int res=0;
    			for (int j=1;j<=n;j++)
    				for (int k=j+1;k<=n;k++) if (ans[j][k]<=c) res++;
    			printf("%d
    ",res);
    		}
    		puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux之文件处理命令
    Linux基础命令
    rip实验
    Linux基础之磁盘分区
    mysql安装
    centos Apache、php、mysql默认安装路径
    You probably tried to upload too large file. Please refer to documentation for ways to workaround this limit.
    Wrong permissions on configuration file, should not be world writable!
    机器会学习么 学习总结
    实验 5 Spark SQL 编程初级实践
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6628716.html
Copyright © 2011-2022 走看看