这道题让我涨姿势了

对于这类问题,我们称作最大权闭合图问题

就是每个点都有一个点权,要求选择一个点集,其中每个点的指向的点也在点集中,使这样一个点权和最大

对于这种问题,我们添加源点s,汇点t

对于点i,如果点权w是正的,我们连边s--->i,流量为w

如果点权w是负的,我们连边i--->t,流量为-w

然后我们我们原图中所有边流量设为inf即可

最后的答案=正点权和-最小割(最大流)

对于这道题,我们把用户和中转站看成点,

对于每个用户,我们显然要由它指向对应的两个中转站,然后做最大权闭合图即可

但是,这样做会tle,

分析一下,为什么呢?因为有多达50000个用户

再仔细观察题目,一共最多只有5000个中转站,

我们知道,最大流的效率取决于找增广路的效率

对于这幅图,我们不难发现,瓶颈边更容易出现在中转站一边

于是我们可以反向建图,这不改变网络的最小割,并且能大大减少增广的次数

反向建图是一个很重要的优化方法

  1 const inf=10000007;
  2 type node=record
  3        next,flow,point:longint;
  4      end;
  5 
  6 var edge:array[0..2000010] of node;
  7     p,h,numh,cur,pre:array[0..60010] of longint;
  8     n,m,i,j,t,x,y,z,len,s:longint;
  9 
 10 function min(a,b:longint):longint;
 11   begin
 12     if a>b then exit(b) else exit(a);
 13   end;
 14 
 15 procedure add(x,y,f:longint);
 16   begin
 17     inc(len);
 18     edge[len].point:=y;
 19     edge[len].flow:=f;
 20     edge[len].next:=p[x];
 21     p[x]:=len;
 22   end;
 23 
 24 function sap:longint;
 25   var u,i,j,q,neck,tmp,s:longint;
 26   begin
 27     u:=0;
 28     numh[0]:=t+1;
 29     sap:=0;
 30     while h[0]<t+1 do
 31     begin
 32       if u=t then
 33       begin
 34         neck:=inf;
 35         i:=0;
 36         while i<>t do
 37         begin
 38           j:=cur[i];
 39           if neck>edge[j].flow then
 40           begin
 41             neck:=edge[j].flow;
 42             s:=i;
 43           end;
 44           i:=edge[j].point;
 45         end;
 46         i:=0;
 47         while i<>t do
 48         begin
 49           j:=cur[i];
 50           dec(edge[j].flow,neck);
 51           inc(edge[j xor 1].flow,neck);
 52           i:=edge[j].point;
 53         end;
 54         sap:=sap+neck;
 55         u:=s;
 56       end;
 57       q:=-1;
 58       i:=p[u];
 59       while i<>-1 do
 60       begin
 61         j:=edge[i].point;
 62         if (edge[i].flow>0) and (h[u]=h[j]+1) then
 63         begin
 64           q:=i;
 65           break;
 66         end;
 67         i:=edge[i].next;
 68       end;
 69       if q<>-1 then
 70       begin
 71         cur[u]:=i;
 72         pre[j]:=u;
 73         u:=j;
 74       end
 75       else begin
 76         dec(numh[h[u]]);
 77         if numh[h[u]]=0 then exit;
 78         tmp:=t+1;
 79         i:=p[u];
 80         while i<>-1 do
 81         begin
 82           j:=edge[i].point;
 83           if edge[i].flow>0 then tmp:=min(tmp,h[j]);
 84           i:=edge[i].next;
 85         end;
 86         h[u]:=tmp+1;
 87         inc(numh[h[u]]);
 88         if u<>0 then u:=pre[u];
 89       end;
 90     end;
 91   end;
 92 
 93 begin
 94   readln(n,m);
 95   t:=n+m+1;
 96   len:=-1;
 97   fillchar(p,sizeof(p),255);
 98   for i:=1 to n do
 99   begin
100     read(x);
101     add(0,i,x);
102     add(i,0,0);
103   end;
104   for i:=1 to m do
105   begin
106     readln(x,y,z);
107     s:=s+z;
108     add(n+i,t,z);
109     add(t,n+i,0);
110     add(x,n+i,inf);
111     add(n+i,x,0);
112     add(y,n+i,inf);
113     add(n+i,y,0);
114   end;
115   writeln(s-sap);
116 end.
View Code

 

posted on 2014-05-27 22:42  acphile  阅读(282)  评论(0编辑  收藏  举报