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 }
posted @ 2018-03-07 13:12  CHerish_OI  阅读(633)  评论(0编辑  收藏  举报