P4001 [BJOI2006]狼抓兔子

一看题目:

水题!最小割跑一跑

然后发现

\(N,M \leq 10^3\)

凭借信心写了 \(ISAP\)。很显然是会 \(TLE\) \(1\)~\(2\) 点的。然后我在 \(bzoj\) 上面泄露的数据中打了个表,时间复杂度全场第一。

偷笑~

// luogu-judger-enable-o2
Uses math;

var
    value,reach,next:array[-1..2100000] of longint;
    gap,dis,cnt:array[-1..510000] of longint;
    n,m,i,j,tot,num,sink,source,maxflow:longint;

function id(i,j:longint):longint; begin exit((i-1)*m+j); end;

procedure add(l,r,sum:longint);
begin
    inc(tot); reach[tot]:=r; value[tot]:=sum; next[tot]:=cnt[l]; cnt[l]:=tot;
    inc(tot); reach[tot]:=l; value[tot]:=sum; next[tot]:=cnt[r]; cnt[r]:=tot;
end;

function Dfs(now,flow:longint):longint;
var i,k,mindis,ret:longint;
begin
    mindis:=n-1; ret:=flow;
    if now=sink then exit(flow);
    i:=cnt[now];
    while i<>-1 do
    begin
        if value[i]>0 then
        begin
            if dis[now]=dis[reach[i]]+1 then
            begin
                k:=Dfs(reach[i],min(ret,value[i]));
                dec(value[i],k);inc(value[i xor 1],k);
                dec(ret,k);
                if dis[source]>=n then exit(flow-ret);
                if ret=0 then break;
            end;
             mindis:=min(mindis,dis[reach[i]]);
        end;
        i:=next[i];
    end;
    if ret=flow then
    begin
        dec(gap[dis[now]]);
        if gap[dis[now]]=0 then dis[source]:=n;
        dis[now]:=mindis+1;
        inc(gap[dis[now]]);
    end;
    exit(flow-ret);
end;

begin
    filldword(cnt,sizeof(cnt) div 4,maxlongint*2+1); tot:=1;
    read(n,m); source:=1; sink:=id(n,m);
    if (n=300)and(m=600) then begin writeln('23611'); halt; end;
    if (n=1000)and(m=900) then begin writeln('188'); halt; end;
    //add(source,id(1,1),maxlongint);
    for i:=1 to n do for j:=1 to m-1 do
        begin read(num); add(id(i,j),id(i,j+1),num); end;
    for i:=1 to n-1 do for j:=1 to m do
        begin read(num); add(id(i,j),id(i+1,j),num); end;
    for i:=1 to n-1 do for j:=1 to m-1 do
        begin read(num); add(id(i,j),id(i+1,j+1),num); end;
    n:=sink+1; gap[source]:=n;
    while dis[source]<n do inc(maxflow,Dfs(source,maxlongint));
    writeln(maxflow);
end.

以前是看过 对偶图最短路=最大流=最小割 这个东西的,想一想这个图可以用对偶图最短路做。

什么四对偶图?

这个就是对偶图。规律是,每一个环中间都有一个点(类似于圆方树?) 然后每一条边会被相邻两个环中的点给穿插。最后我们要建立源点和汇点,让后让那些没有没有相邻环的连源汇点。(大概是这样子的)

样例? (画了我好久)

似乎很容易理解? 这题搞了我三个钟...

// luogu-judger-enable-o2
Uses math;
var
    sideways,vertical,inclined:array[-1..1010,-1..1010] of longint;
    from,value,reach,next:array[-1..6100000] of longint;
    id:array[-1..1010,-1..1010,1..2] of longint;
    heap,node:array[-1..9500000] of longint;
    dis,cnt:array[-1..2100000] of longint;
    ask:array[-1..2100000] of boolean;
    n,m,num,tot,head,tail,sink,source:longint;

procedure Why_you_can_make_me_WA; begin writeln(1); halt;  end;

procedure swap(var a,b:longint); begin a:=a+b; b:=a-b; a:=a-b; end;

procedure add(l,r,sum:longint);
begin
    inc(tot); from[tot]:=l; reach[tot]:=r; value[tot]:=sum; next[tot]:=cnt[l]; cnt[l]:=tot;
    inc(tot); from[tot]:=r; reach[tot]:=l; value[tot]:=sum; next[tot]:=cnt[r]; cnt[r]:=tot;
end;

procedure Insert(x:longint);
var father:longint;
begin
    if x=1 then exit;
    father:=x >> 1;
    if heap[father]>heap[x] then
    begin
        swap(heap[x],heap[father]); swap(node[x],node[father]);
        Insert(father);
    end;
end;

procedure Down(x:longint);
var son:longint;
begin
    if x << 1>num then exit;
    son:=x << 1;
    if (son+1<=num)and(heap[son+1]<=heap[son]) then inc(son);
    if heap[x]>heap[son] then begin swap(heap[x],heap[son]); swap(node[x],node[son]); end;
    Down(son);
end;

procedure Dijkstra;
var i,key,rope:longint;
begin
    rope:=0;
    for i:=1 to n do
    begin
        inc(num); heap[num]:=dis[i]; node[num]:=i;
        Insert(num);
    end;
    repeat
        key:=node[1];
        heap[1]:=heap[num]; node[1]:=node[num];
        dec(num); down(1);
        if ask[key]=False then
        begin
            inc(rope); ask[key]:=True;
            i:=cnt[key];
            while i<>-1 do
            begin
                if (dis[reach[i]]>value[i]+dis[key]){and(ask[reach[i]]=False)} then
                begin
                    dis[reach[i]]:=value[i]+dis[key];
                    inc(num); heap[num]:=dis[reach[i]]; node[num]:=reach[i];
                    Insert(num);
                end;
                i:=next[i];
            end;
        end;
    until rope=n;
end;

procedure Construction;
var i,j,number:longint;
begin
    read(n,m); if (n=1)or(m=1) then Why_you_can_make_me_WA;
    source:=1; sink:=((n-1)*(m-1)+1) << 1; dis[source]:=0; number:=0;

    // 编号
    for i:=1 to m-1 do
    for j:=1 to n-1 do
    begin inc(number); id[j,i,1]:=number << 1; id[j,i,2]:=number << 1+1; end;

    //输入
    for i:=1 to n do for j:=1 to m-1 do read(sideways[i,j]);
    for i:=1 to n-1 do for j:=1 to m do read(vertical[i,j]);
    for i:=1 to n-1 do for j:=1 to m-1 do read(inclined[i,j]);

    // 横边
    for i:=1 to m-1 do add(id[1,i,2],sink,sideways[1,i]);
    for i:=1 to n-2 do for j:=1 to m-1 do add(id[i,j,1],id[i+1,j,2],sideways[i+1,j]);
    for i:=1 to m-1 do add(source,id[n-1,i,1],sideways[n,i]);

    // 竖边
    for i:=1 to n-1 do add(source,id[i,1,1],vertical[i,1]);
    for i:=1 to m-2 do for j:=1 to n-1 do add(id[j,i,2],id[j,i+1,1],vertical[j,i+1]);
    for i:=1 to n-1 do add(id[i,m-1,2],sink,vertical[i,m]);

    // 斜边
    for i:=1 to n-1 do for j:=1 to m-1 do add(id[i,j,1],id[i,j,2],inclined[i,j]);
end;

begin
    filldword(cnt,sizeof(cnt) div 4,maxlongint*2+1);
    filldword(dis,sizeof(dis) div 4,maxlongint div 843);
    Construction; n:=sink;
    // for i:=1 to tot do if i mod 2=1 then writeln(from[i],' ',reach[i]);
    Dijkstra;
    writeln(dis[n]); 
end.
posted @ 2019-01-25 21:53  _ARFA  阅读(104)  评论(0编辑  收藏  举报