1. 程式人生 > >最小截斷[AHOI2009]

最小截斷[AHOI2009]

top spl main ems 控制 存在 bsp queue 擴大

【題目描述】

宇宙旅行總是出現一些意想不到的問題,這次小可可所駕駛的宇宙飛船所停的空間站發生了故障,這個宇宙空間站非常大,它由N個子站組成,子站之間有M條單向通道,假設其中第i(1<=i<=M)條單向通道連接了xi,yi兩個中轉站,那麽xi子站可以通過這個通道到達yi子站,如果截斷這條通道,需要代價ci。現在為了將故障的代價控制到最小,小可可必須想出一個截斷方案,使a站不能到達b子站,並且截斷的代價之和最小。我們稱之為最小截斷,小可可很快解決了這個故障,但是愛思考的小可可並不局限於此,為了今後更方便的解決同類故障,他考慮對每條單向通道:

1,是否存在一個最小代價路徑截斷方案,其中該通道被切斷?

2,是否對任何一個最小代價路徑切斷方案,都有該通道被切斷?

聰明的你能幫小可可解決他的疑問嗎?

【輸入格式】

第一行有4個整數,依次為N,M,a和b;

第二行到第(m+1)行每行3個正整數x,y,c表示x子站到y子站之間有單向通道相連,單向通道的起點是x終點是y,切斷它的代價是c(1<=c<=10000);

兩個子站之間可能有多條通道直接連接。

【輸出格式】

對每一個單向通道,按輸入的順序,依次輸出一行包含兩個非0即1的整數,分別表示對問題一和問題二的回答(其中1表示是,0表示否)。每行兩個整數之間用一個空格分隔開。

【樣例輸入】

   6 7 1 6 
   1 2 3
   1 3 2
   2 4 4 
   2 5 1
   3 5 5 
   4 6 2
   5 6 3

【樣例輸出】

1 0
1 0
0 0
1 0
0 0
1 0
1 0

【提示】

100%的數據中,N<=4000,M<=60000

70%的數據中,N<=1000,M<=40000

40%的數據中,N<=200,M<=2000

【題解】

血帆海盜的進階版,先貼結論XDDD:

最小割的必須邊
一定在最小割中的邊、擴大容量後能增大最大流的邊, ① 滿流;② 殘余網絡中S能到入點、出點能到T。 從S開始DFS、T開始反向DFS,標記到達的點,然後枚舉滿流邊即可。
最小割的可行邊
被某一種最小割的方案包含的邊, ① 滿流;② 刪掉之後在殘余網絡中找不到u到v的路徑。 在殘余網絡中tarjan求SCC,(u,v)兩點在同一SCC中說明殘余網絡中存在u到v路徑。

在這道題裏求必須邊也可以用tarjan搞定,必須邊的起點與S在同一個強聯通分量裏,終點與T在同一個強聯通分量裏。知道了結論之後就可以放心地跑了。

技術分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;
const int sj=4010;
int n,m,s,t,e,a1,a2,a3;
int dep[sj],dfn[sj],c[sj],low[sj],h[sj];
stack<int> z;
queue<int> q;
bool r[sj];
struct B
{
    int ne,v,w,u;
}b[120010];
void add(int x,int y,int z)
{
     b[e].v=y;
     b[e].u=x;
     b[e].w=z;
     b[e].ne=h[x];
     h[x]=e++;
     b[e].v=x;
     b[e].w=0;
     b[e].u=y;
     b[e].ne=h[y];
     h[y]=e++;
}
bool bfs(int x)
{
     while(!q.empty()) q.pop();
     memset(dep,0,sizeof(dep));
     dep[x]=1;
     q.push(x);
     while(!q.empty())
     {
        x=q.front();
        q.pop();
        for(int i=h[x];i!=-1;i=b[i].ne)
          if(b[i].w&&!dep[b[i].v])
          {
             dep[b[i].v]=dep[x]+1;
             if(b[i].v==t) return 1;
             q.push(b[i].v);
          }
     }
     return 0;
}
int bj(int x,int y)
{
    return x<y?x:y;
}
int dfs(int x,int f)
{
    if(x==t)  return f; 
    int ans=0,d;
    for(int i=h[x];i!=-1;i=b[i].ne)
      if(dep[b[i].v]>dep[x]&&b[i].w)
      {
         d=dfs(b[i].v,bj(b[i].w,f));
         f-=d;
         ans+=d;
         b[i].w-=d;
         b[i^1].w+=d;
         if(!f) break;
      }
    if(!ans) dep[x]=-1;
    return ans;
}
void tarjan(int x)
{
     low[x]=dfn[x]=++a1;
     z.push(x);
     r[x]=1;
     for(int i=h[x];i!=-1;i=b[i].ne)
     {
        if(!b[i].w) continue;
        if(!dfn[b[i].v])
        {
           tarjan(b[i].v);
           low[x]=bj(low[x],low[b[i].v]);
        }
        else if(r[b[i].v])
           low[x]=bj(low[x],dfn[b[i].v]);
     }
     if(low[x]==dfn[x])
     {
        a2++;
        do
        {
           a3=z.top();
           z.pop();
           c[a3]=a2;
           r[a3]=0;
        }while(a3!=x);
     }
}
void init()
{
     scanf("%d%d%d%d",&n,&m,&s,&t);
     memset(h,-1,sizeof(h));
     for(int i=1;i<=m;i++)
     {
       scanf("%d%d%d",&a1,&a2,&a3);
       add(a1,a2,a3);
     }
     while(bfs(s)) dfs(s,0x7fffffff);
     a1=a2=0;
     for(int i=1;i<=n;i++)
       if(!dfn[i]) tarjan(i);
     s=c[s];
     t=c[t];
}
void cl()
{
     for(int i=0;i<e;i+=2)
     {
        if(b[i].w) printf("0 0\n");
        if(!b[i].w)
        {
           if(c[b[i].u]!=c[b[i].v])
           {
              if(c[b[i].u]==s&&c[b[i].v]==t)
                printf("1 1\n");
              else printf("1 0\n");
           } 
           else printf("0 0\n");
        }
     }
}
int main()
{
    init();
    cl();
    return 0;
}
mincut

最小截斷[AHOI2009]