【bzoj2229】 Zjoi2011—最小割
2017-03-27 20:46
453 查看
http://www.lydsy.com/JudgeOnline/problem.php?id=2229 (题目链接)
最小割树什么的好神,但是看不懂啊,不如直接撸代码= =。根据网上神犇的理论,貌似最小割的数目不会超过$n-1$个,所以可以将它构成一棵最小割树。
不过我们的实现并不需要考虑怎么构树。直接暴力的话就是枚举点对,要做$n^2$次$Dinic$,我们通过选择一些优秀的点对来减少$Dinic$的次数。每次分治,任选两个在当前分治区间中的点作为源点和汇点,在原图上做一次$Dinic$,将原图分为了两个割集$S$和$T$,更新$S$和$T$之间的点的最小割。将这两个割集与分治区间取交得到分值区间的割集$S'$和$T'$,然后递归处理$S'$和$T'$就可以了。
值得注意的是,这样子并没有减小问题的规模,只是通过有技巧的选择源点和汇点来减少$Dinic$的次数(虽然我也不知道为什么这样是正确的)。复杂度大概是$O(kn*Dinic)$,$k$这个常数应该不会太大,出题人总不会丧心病狂卡这玩意儿吧,大不了random_shuffle一下= =。
题意
给出一张无向图,$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\n",res); } puts(""); } return 0; }
相关文章推荐
- bzoj2229: [Zjoi2011]最小割
- bzoj 2229: [Zjoi2011]最小割 分治&网络流
- BZOJ 2229: [Zjoi2011]最小割
- bzoj 2229: [Zjoi2011]最小割【Gomory–Hu tree最小割树】
- BZOJ 2229 ZJOI2011 最小割 最小割+分治 400AC达成&&2000Submission达成
- bzoj 2229 [Zjoi2011]最小割(分治+最小割)
- bzoj 2229: [Zjoi2011]最小割 分治最小割(最小割树)
- [BZOJ2229][Zjoi2011]最小割(最小割+分治)
- BZOJ 2229 [Zjoi2011] 最小割
- bzoj2229 [Zjoi2011]最小割(最小割树,分治)
- BZOJ 2521: [Shoi2010]最小生成树&&2229: [Zjoi2011]最小割
- bzoj2229 [Zjoi2011]最小割
- 【ZJOI2011】【BZOJ2229】最小割
- 【BZOJ2229】【ZJOI2011】最小割 {没有错,这道题的算法跟题帽是一样的!!!}
- 【bzoj2229】[Zjoi2011]最小割 分治+网络流最小割
- [BZOJ2229][ZJOI2011]最小割
- bzoj 2229: [Zjoi2011]最小割
- BZOJ 2229: [Zjoi2011]最小割 最小割树 / 分治最小割
- bzoj 2229: [Zjoi2011]最小割
- BZOJ2229: [Zjoi2011]最小割 分治最小割