最小截斷[AHOI2009]
【題目描述】
宇宙旅行總是出現一些意想不到的問題,這次小可可所駕駛的宇宙飛船所停的空間站發生了故障,這個宇宙空間站非常大,它由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]