bzoj 2561 最小割

11 篇文章 0 订阅
10 篇文章 0 订阅

题意:给定一个n个点m个无向联通图,再给出一条边,最少删除多少条边使得这条边既出现在最小生成树上又出现在最大生成树上

...说实话,这数据范围有点吓人,瞬间不知所措...._(:3 」∠)_ .

对于某一条边,如果边权小于它的边能使其两个端点连通,则这条边一定不会出现在最小生成树中。

对于某一条边,如果边权大于它的边能使其两个端点连通,则这条边一定不会出现在最大生成树中。

所以,

原题可变成:删去最少的边,使小于新边边权的边不能使两端点连通、大于该边权的边也不能使两端点连通。

妥妥的最小割....

先把所有小于新边边长的边,设边权为1建图来一遍最小割

再把所有大于新边边长的边,设边权为1建图来一遍最小割

这样最小割即为要删去的边的条数,对两次结果取和即为最终答案

注意重建图之前的清零...

据说,dinic对于容量为1的网络时间复杂度为O(min(N^(2/3),M^(1/2))*M),还是可以的..._(:3 」∠)_

type
        rec=record
            a,b,len:longint;
end;

{$ inline on}
var
        n,m,ll,x,y,z    :longint;
        ss,st,tt,ans    :longint;
        l               :array[0..200010] of rec;
        last,que,dis    :array[0..20010] of longint;
        len,other,pre   :array[0..400010] of longint;
        i               :longint;
function min(a,b:longint):longint;inline;
begin
   if a<b then exit(a) else exit(b);
end;

procedure connect(x,y,z:longint);inline;
begin
   inc(ll);
   pre[ll]:=last[x];
   last[x]:=ll;
   other[ll]:=y;
   len[ll]:=z;
end;

procedure sort(ll,rr:longint);
var
        i,j,x:longint;
        y:rec;
begin
   i:=ll; j:=rr; x:=l[(ll+rr)>>1].len;
   while (i<=j) do
   begin
      while l[i].len<x do inc(i);
      while l[j].len>x do dec(j);
      if (i<=j) then
      begin
         y:=l[i]; l[i]:=l[j]; l[j]:=y;
         inc(i); dec(j);
      end;
   end;
   if i<rr then sort(i,rr);
   if j>ll then sort(ll,j);
end;

function bfs:boolean; inline;
var
        p,q,cur,h,tl:longint;
begin
   fillchar(dis,sizeof(dis),0);
   h:=0; tl:=1; que[1]:=ss; dis[ss]:=1;
   while (h<>tl) do
   begin
      h:=h mod 20005+1;
      cur:=que[h];
      q:=last[cur];
      while (q<>0) do
      begin
         p:=other[q];
         if (dis[p]=0) and (len[q]>0) then
         begin
            dis[p]:=dis[cur]+1;
            tl:=tl mod 20005+1;
            que[tl]:=p;
            if p=st then exit(true);
         end;
         q:=pre[q];
      end;
   end;
   exit(false);
end;

function dinic(x,flow:longint):longint;
var
        rest,tt,p,q:longint;
begin
   if x=st then exit(flow);
   rest:=flow;
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if (dis[p]=dis[x]+1) and (len[q]>0) and (rest>0) then
      begin
         tt:=dinic(p,min(rest,len[q]));
         dec(len[q],tt);
         inc(len[q xor 1],tt);
         dec(rest,tt);
         if rest=0 then exit(flow);
      end;
      q:=pre[q];
   end;
   if rest=flow then dis[x]:=0;
   exit(flow-rest);
end;

begin
   read(n,m);
   for i:=1 to m do read(l[i].a,l[i].b,l[i].len);
   read(ss,st,tt);
   sort(1,m);
   ll:=1; ans:=0;
   for i:=1 to m do
     if l[i].len<tt then
     begin
        connect(l[i].a,l[i].b,1);
        connect(l[i].b,l[i].a,1);
     end else break;
   while bfs do inc(ans,dinic(ss,maxlongint div 10));
   ll:=1;
   fillchar(last,sizeof(last),0); 
   for i:=m downto 1 do
     if l[i].len>tt then
     begin
        connect(l[i].a,l[i].b,1);
        connect(l[i].b,l[i].a,1);
     end else break;
   while bfs do inc(ans,dinic(ss,maxlongint div 10));
   writeln(ans);
end.
——by Eirlys



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值