bzoj 1585 && 洛谷 2932 【usaco】Earthquake Damage 2&&1

25 篇文章 0 订阅
10 篇文章 0 订阅
洛谷 2932  Earthquake Damage1
题意:n个点,m条边,其中一些节点发生了损坏。当一个点当到一号点所有路径都必经被损坏或该点被损坏时,该点与1号点不能联通。已知p个完好但不能与1号点联通的点,问n个点钟,所有不能与1联通的点的个数最少是多少

注意不要理解错题意,题目只要求到一号点所有路径必经被损坏的点即可而不是所有路径上的点都被损坏(我是zz...)
由于它要求不能与1联通的点的个数最少,所以我们要在满足p个点与1不连通的情况下对其他点造成的影响最小
贪心,只需要把与那p个点直接连接的点全都损坏即可

然后再从1 dfs一遍统计损坏后新的不能到达1号点的点的个数

var
        n,m,p,l,ans,x,y :longint;
        i               :longint;
        last            :array[0..30010] of longint;
        vis             :array[0..30010] of boolean;
        flag            :array[0..30010] of longint;
        pre,other       :array[0..200010] of longint;

procedure connect(x,y:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
end;

procedure work(x:longint);
var
        p,q:longint;
begin
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      flag[p]:=1;
      q:=pre[q];
   end;
end;

procedure dfs(x:longint);
var
        p,q:longint;
begin
   if flag[x]=1 then exit;
   vis[x]:=true;
   flag[x]:=2;
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if not vis[p] then dfs(p);
      q:=pre[q];
   end;
end;

begin
   read(n,m,p);
   for i:=1 to m do
   begin
      read(x,y);
      connect(x,y);
      connect(y,x);
   end;
   for i:=1 to p do
   begin
      read(x);
      flag[x]:=1;
      work(x);
   end;
   dfs(1);
   ans:=0;
   for i:=1 to n do
     if flag[i]<>2 then inc(ans);
   writeln(ans);
end.


bzoj 1585 Earthquake Damage2

题意:n个点,m条完好的边,某些点被损坏。已知p个完好但无法与1联通的点,当一个点到1点的所有路径都必经被损坏的点,这个点与1点不连通(和上一题定义一样),求最少损坏了多少点

做完上一题马上就去做这道题,看了眼数据范围又入了贪心的坑,然而这题用贪心真的是个坑....

一个错的naive的贪心:这一题要求被损坏的最少,也就是找一些点让它尽可能多的影响这p个点,然后类比上一题,所以这一题就把和1直接相连的且能到达p个点中的点的点损坏即可

然而在wa了一发后就发现这并不能保证最优...(因为可能不用去损坏两个与1直接相连的符合要求的点而去损坏一个交点)

 然后再看一眼题,这不就是割去最少的点然后让1和p个点全都不连通...不就是最小割模板么= =

看了眼数据范围略虚但还是要相信玄学dinic...

因为我们是割去点而不是割去边,所以我们拆点,把一个点拆成x和x‘,中间连一条边,这样割去点x就变成了割去这条边

建图:

让汇 T=1

(1)S ->p个提到的点x 容量为正无穷

(2)p个提到的点x -> x‘(自己拆的另一个点) 容量为正无穷(表示这个点不能割,因为题目要求这p个点是完好的)

(3)n个点中除了1号店和p个提到的点 x-> x' 容量为1(表示这些点是可以割去的,且只可以割去一次)

(4)原图m条边x、y   

①如果x和y都不是汇   x‘->y y'->x 容量为正无穷 (表示这些是原图的边,而我们割去的是点,所以这些边是不能损坏的,同时这也是题目要求) 

②如果x是汇  y‘->1 容量为正无穷

③如果y是汇  x'->1 容量为正无穷

var
       n,m,p,x,y,ss,st,l:longint;
       ans              :longint;
       i                :longint;
       last             :array[0..6010] of longint;
       que,dis          :array[0..6010] of longint;
       flag             :array[0..3010] of boolean;
       pre,other,len    :array[0..90010] of longint;

function min(a,b:longint):longint;
begin
   if a<b then exit(a) else exit(b);
end;

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

function bfs:boolean;
var
        i,h,tl,p,q,cur:longint;
begin
   for i:=1 to ss do dis[i]:=0;
   h:=0; tl:=1; que[1]:=ss; dis[ss]:=1;
   while h<>tl do
   begin
      h:=h mod 6005+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 6005+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
        tt,rest,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(len[q],rest));
         dec(len[q],tt);
         dec(rest,tt);
         inc(len[q xor 1],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,p);
   l:=1; ss:=2*n+1; st:=1;
   for i:=1 to m do
   begin
      read(x,y);
      if x=1 then
      begin
         connect(2*y,1,maxlongint div 10);
         connect(1,2*y,0);
      end else
      if y=1 then
      begin
         connect(2*x,1,maxlongint div 10);
         connect(1,2*x,0);
      end else
      begin
         connect(2*x,2*y-1,maxlongint div 10);
         connect(2*y-1,2*x,0);
         connect(2*y,2*x-1,maxlongint div 10);
         connect(2*x-1,2*y,0);
      end;
   end;
   for i:=1 to p do
   begin
      read(x);
      flag[x]:=true;
      connect(2*x-1,2*x,maxlongint div 10);
      connect(2*x,2*x-1,0);
      connect(ss,2*x-1,maxlongint div 10);
      connect(2*x-1,ss,0);
   end;
   for i:=2 to n do
     if not flag[i] then
     begin
        connect(2*i-1,2*i,1);
        connect(2*i,2*i-1,0);
     end;
   ans:=0;
   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、付费专栏及课程。

余额充值