bzoj2229: [Zjoi2011]最小割(分治最小割+最小割树思想)

2229: [Zjoi2011]最小割

题目:传送门

题解:

   一道非常好的题目啊!!!

   蒟蒻的想法:暴力枚举点对跑最小割记录...绝对爆炸啊....

   开始怀疑是不是题目骗人...难道根本不用网络流???一看路牌....分治最小割?最小割树?

   然后开始各种%论文...

   简单来说吧,根据各种本蒟蒻不会证明的理论,那么:所有最小割都不是完全独立的,总共有n-1种(也就是树上的n-1条边)最小割 恰好和树的定义一样啊!

   那么用一个solve递归函数来解决,一开始任意找两个点作为st和ed来最小割,然后分治,在分开的两个集合中继续递归,用数组记录答案

   以上都是根据边所进行的离线操作,然后输入Q个询问,直接在线输出答案就ok

代码:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cmath>
  5 #include<algorithm>
  6 using namespace std;
  7 const int inf=999999999;
  8 struct node
  9 {
 10     int x,y,c,next,other;
 11 }a[210000];int len,last[110000];
 12 int st,ed,n,m,T,ans;
 13 void ins(int x,int y,int c)
 14 {
 15     int k1,k2;
 16     k1=++len;
 17     a[len].x=x;a[len].y=y;a[len].c=c;
 18     a[len].next=last[x];last[x]=len;
 19
 20     k2=++len;
 21     a[len].x=y;a[len].y=x;a[len].c=c;
 22     a[len].next=last[y];last[y]=len;
 23
 24     a[k1].other=k2;
 25     a[k2].other=k1;
 26 }
 27 int list[110000],h[110000],head,tail;
 28 bool bt_h()
 29 {
 30     memset(h,0,sizeof(h));h[st]=1;
 31     list[1]=st;head=1;tail=2;
 32     while(head!=tail)
 33     {
 34         int x=list[head];
 35         for(int k=last[x];k;k=a[k].next)
 36         {
 37             int y=a[k].y;
 38             if(h[y]==0 && a[k].c)
 39             {
 40                 h[y]=h[x]+1;
 41                 list[tail++]=y;
 42             }
 43         }
 44         head++;
 45     }
 46     if(h[ed])return true;
 47     return false;
 48 }
 49 int find_flow(int x,int flow)
 50 {
 51     if(x==ed)return flow;
 52     int s=0,t;
 53     for(int k=last[x];k;k=a[k].next)
 54     {
 55         int y=a[k].y;
 56         if(h[y]==h[x]+1 && a[k].c && s<flow)
 57         {
 58             s+=t=find_flow(y,min(a[k].c,flow-s));
 59             a[k].c-=t;a[a[k].other].c+=t;
 60         }
 61     }
 62     if(s==0)h[x]=0;
 63     return s;
 64 }
 65 int d[110000],num[110000],sta[110000],anss[1100][1100];
 66 void re_num(int x)
 67 {
 68     num[x]=1;
 69     for(int k=last[x];k;k=a[k].next)
 70     {
 71         int y=a[k].y;
 72         if(a[k].c && !num[y])
 73             re_num(y);
 74     }
 75 }
 76 void solve(int l,int r)
 77 {
 78     if(l==r)return ;
 79     for(int i=1;i<=len;i+=2)a[i].c=a[i+1].c=(a[i].c+a[i+1].c)>>1;//将边权重置
 80     st=d[l];ed=d[r];ans=0;
 81     while(bt_h())ans+=find_flow(st,inf);
 82     memset(num,0,sizeof(num));
 83     re_num(st);
 84     for(int i=1;i<=n;i++)
 85         if(num[i])
 86             for(int j=1;j<=n;j++)
 87                 if(!num[j])
 88                     anss[i][j]=anss[j][i]=min(anss[i][j],ans);
 89     int L=l,R=r;
 90     for(int i=l;i<=r;i++)
 91     {
 92         if(num[d[i]])sta[L++]=d[i];
 93         else      sta[R--]=d[i];
 94     }
 95     for(int i=l;i<=r;i++)d[i]=sta[i];
 96     solve(l,L-1);solve(R+1,r);//分治,递归
 97 }
 98 int main()
 99 {
100     scanf("%d",&T);
101     while(T--)
102     {
103         scanf("%d%d",&n,&m);
104         len=0;memset(last,0,sizeof(last));int x,y,c;
105         for(int i=1;i<=m;i++)scanf("%d%d%d",&x,&y,&c),ins(x,y,c);
106         for(int i=1;i<=n;i++)d[i]=i;memset(anss,63,sizeof(anss));
107         solve(1,n);
108         int Q;scanf("%d",&Q);
109         while(Q--)
110         {
111             scanf("%d",&x);int cnt=0;
112             for(int i=1;i<=n;i++)
113                 for(int j=i+1;j<=n;j++)
114                     if(anss[i][j]<=x)
115                         cnt++;
116             printf("%d\n",cnt);
117         }
118         printf("\n");
119     }
120     return 0;
121 }

原文地址:https://www.cnblogs.com/CHerish_OI/p/8521791.html

时间: 2024-08-28 19:47:09

bzoj2229: [Zjoi2011]最小割(分治最小割+最小割树思想)的相关文章

[bzoj2229][Zjoi2011]最小割_网络流_最小割树

最小割 bzoj-2229 Zjoi-2011 题目大意:题目链接. 注释:略. 想法: 在这里给出最小割树的定义. 最小割树啊,就是这样一棵树.一个图的最小割树满足这棵树上任意两点之间的最小值就是原图中这两点之间的最小割. 这个性质显然是非常优秀的. 我们不妨这样假设,我么已经把最小割树求出来了,那么这个题就迎刃而解了. 我们可以直接枚举点对,然后暴力验证就可以直接枚举出所有的合法点对是吧. 那么问题来了,我们如何才能求出所有的合法的点对? 这就需要用到了最小割树的构建过程. 我们最小割树的构

最小割分治(最小割树):BZOJ2229 &amp;&amp; BZOJ4519

定理:n个点的无向图的最小割最多n-1个. 可能从某种形式上形成了一棵树,不是很清楚. 最小割分治:先任选两个点求一边最小割,然后将两边分别递归,就能找到所有的最小割. 这两个题是一样的,直接搬dinic模板即可. BZOJ2229: 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #define mem(a,k) memset(a,k,siz

BZOJ 2229 ZJOI2011 最小割 最小割+分治 400AC达成&amp;&amp;2000Submission达成

题目大意:给定一个图,多次询问有多少个点对之间的最小割小于等于某个值 最小割分治- - 首先朴素的想法是做O(n^2)遍网络流 但是这样显然是过不去的 根据一些结论,最小割最多有n-1个,这n-1个最小割构成一个最小割树 别问我为什么- - 因此我们分治寻找这n-1个最小割 每层分治,先任选两个点作为源汇做一遍最小割 然后找出S集和T集,对所有S集的点和T集的点构成的点对用本次得到的最小割更新一遍 注意更新的是全部S集和全部T集,不只是本次分治内部的S集和T集 然后将本次分治的点分成S集和T集,

hdoj 3251 Being a Hero 【建图后求解最小割 + 输出任意一组最小割里面边 的编号】

Being a Hero Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1252    Accepted Submission(s): 395 Special Judge Problem Description You are the hero who saved your country. As promised, the ki

POJ 3308 Paratroopers 最小点权覆盖 求最小割

不懂这个建模是什么原理,以后把二分图相关的东西看完再补上把= = #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #i

最大匹配、最小顶点覆盖、最大独立集、最小路径覆盖(转)

在讲述这两个算法之前,首先有几个概念需要明白: 二分图: 二分图又称二部图,是图论中的一种特殊模型.设G=(V,E)是一个无向图,如果顶点V可以分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A, j in B), 则称图G是二分图. 匹配: 给定一个二分图,在G的一个子图G'中,如果G'的边集中的任意两条边都不依附于同一个顶点,则称G'的边集为G的一个匹配 最大匹配: 在所有的匹配中,边数最多的那个匹配就是二分图的最大匹

HDU 4862 Jump (2014-多校1-1002,最小K路径覆盖,最小费用最大流)

题目: http://acm.hdu.edu.cn/showproblem.php?pid=4862 题意: 给你一个n*m的矩阵,填充着0-9的数字,每次能从一个点出发,到它的右边或者下边的点,花费为|x1-x2|+|y1-y2|-1,如果跳跃的起点和终点的数字相同,则获得这个数字的收益,不能走已经走过的点 有K次重新选择起点的机会 如果可以走遍所有点,则输出最大的价值(收益-花费) 否则,输出-1 方法: 最小K路径覆盖,最小费用最大流 建图: 每个点拆为2点:X部和Y部,(a,b)表示流量

二分图最大匹配,最小路径覆盖,最小点覆盖,最大独立集,最小边覆盖与建图方法

转载请注明出处(别管写的好坏,码字也不容易):http://blog.csdn.net/hitwhacmer1 前言:         有自己写的,有摘的别人的,前面是摘的,也是无心整理,出错是难免的,反正我都不会证明,智人见智,别被我误导了. §1图论点.边集和二分图的相关概念和性质 点覆盖.最小点覆盖 点覆盖集即一个点集,使得所有边至少有一个端点在集合里.或者说是"点" 覆盖了所有"边"..极小点覆盖(minimal vertex covering):本身为点覆

[BZOJ2229][ZJOI2011]最小割

bzoj luogu sol 最小割树请转一道很相似完全一模一样的题 所以跑出所有点对之间的最小割然后暴力统计答案即可. code #include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; int gi() { int x=0,w=1;char ch=getchar(); while ((ch<'0'||ch>'9')&a