【GDOI2004】破坏行动

Description

恐怖组织首领本拉登打算摧毁一个敌对势力的石油运输系统。这个石油运输系统可以看成是一个运输网络,由许多的节点和连接节点的管道构成。只有地点A生产石油,而生产的石油则通过管道输送到地点B,石油不能在中间节点累积。管道是双向的,每条管道有一个抗压指数,当石油的流量超过这个数时管道就会爆炸。A地生产石油的速度可以认为是非常快的,但由于管道抗压指数的原因,能运到B的有一个上限。本拉登知道敌对势力采用了某种方案使得他们能输送最多的石油,但他不知道这个具体的方案使什么。本拉登有一种特殊的物质,可以让一条管道的抗压指数下降1。作为恐怖组织的首席程序员,你的任务就是告诉本拉登,让哪些管道的抗压指数下降,一定可以使管道爆炸从而摧毁运输网络。
Input

第一行包含四个整数n m s t,表示有n个节点(编号为1,2,3….n),m条管道,s t分别是A地和B地的编号。2<=n<=130,0<=m<=n*(n-1)/2,1<=s,t<=n.
接下来m行每行描述一条管道,包含三个整数i j c.i和j分别为管道连接的两个节点,c为这条管道的抗压指数。1<=I,j<=n,1<=c<=10000.

Output

第一行输出抗压指数减少1就必定爆炸的管道的条数k.
接下来k行每行输出一个整数p(1<=p<=m),说明第p条管道如果抗压指数减少1就必定爆炸。序号p按照管道输入的顺序,并按照p的升序输出。

分析

这题的题意就是给你一张图,每条边上有流量,类似网络流一样流。问存在哪些边是一定在最大流里面的,即一定出现在最大流里面。
对于这样的一个问题,我们在残量网络里面研究,那么若一条边一定存在,那么这条边在残量网络里面一定是满流的。
当然除了这一点,还要满足其他的条件。就如题目上说的,容量减少1,判断是否还是原来的流量即可。但是我们这样去“爆搜”是会超时的。所以我们考虑其他的方法。
我们从“一定存在”定义入手:也就是说这条边(u,v),u->v一定只能u->v。不能经过其他的边走到v。我们从这一点入手:若存在经过其他的边到达v,便不是一定存在。
所以我们只需根据残量网络来做floyd,然后判断是否联通就可以了。

var
    n,m,st,en,i,j,k,ans,z:longint;an,x,y:array[1..16900] of longint;
    h,d:array[0..1000] of longint;
    dis,f:array[1..130,1..130] of longint;
function min(l,r:longint):longint;begin if l<r then exit(l) else exit(r);end;
function max(l,r:longint):longint;begin if l>r then exit(l) else exit(r);end;
function ditch(x,y:longint):longint;
var i,mih,t:longint;
begin
     if x=en then exit(y);
     mih:=n+1;
     for i:=1 to n do
     if f[x,i]>0 then begin
        if h[x]=h[i]+1 then begin
           t:=ditch(i,min(f[x,i],y));
           if t>0 then begin
              dec(f[x,i],t);
              inc(f[i,x],t);
              exit(t);
           end;
           if h[st]>n then exit(0);
        end;
        mih:=min(mih,h[i]+1);
     end;
     dec(d[h[x]]);
     if d[h[x]]=0 then h[st]:=n+1;
     h[x]:=mih;
     inc(d[h[x]]);
     exit(0);
end;
begin
    readln(n,m,st,en);
    for i:=1 to m do begin
        read(x[i],y[i],z);
        f[x[i],y[i]]:=z;
        f[y[i],x[i]]:=z;
    end;
    d[0]:=n;
    while h[st]<=n do ans:=ans+ditch(st,maxlongint);
    for i:=1 to n do
        for j:=1 to n do dis[i,j]:=maxlongint div 3;
    for i:=1 to m do begin
    if f[x[i],y[i]]<>0 then dis[x[i],y[i]]:=0;
    if f[y[i],x[i]]<>0 then dis[y[i],x[i]]:=0;
    end;
    for i:=1 to n do
        for j:=1 to n do
        if i<>j then
            for k:=1 to n do
            if (j<>k)and(k<>i) then begin
              dis[j,k]:=min(dis[j,k],dis[j,i]+dis[i,k]);
            end;
    ans:=0;
    for i:=1 to m do begin
    if (f[x[i],y[i]]=0)and(dis[x[i],y[i]]<>0) then begin inc(ans);an[ans]:=i;end;
    if (f[y[i],x[i]]=0)and(dis[y[i],x[i]]<>0) then begin inc(ans);an[ans]:=i;end;
    end;
    writeln(ans);
    for i:=1 to ans do writeln(an[i]);
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值