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;
    }
    
  • 相关阅读:
    [WC2011]最大XOR和路径 线性基
    线段树分裂合并
    [NOIp2016]天天爱跑步 线段树合并
    CF1111E Tree 树链剖分,DP
    [NOI2016]区间 线段树
    [IOI2018] werewolf 狼人 kruskal重构树,主席树
    [CQOI2012]组装 贪心
    [ONTAK2010]Peaks kruskal重构树,主席树
    [NOI2018]归程 kruskal重构树
    kruskal重构树
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6628716.html
Copyright © 2011-2022 走看看