BZOJ1797: [Ahoi2009]Mincut最小割-tarjan缩点&&SAP求最大流

有任何问题欢迎留言或私聊

题目链接:BZOJ1797-点击做题


  

题目:

Description

 A,B两个国家正在交战,其中A国的物资运输网中有N个中转站,M条单向道路。设其中第i (1≤i≤M)条道路连接了vi,ui两个中转站,那么中转站vi可以通过该道路到达ui中转站,如果切断这条道路,需要代价ci。现在B国想找出一个路径切断方案,使中转站s不能到达中转站t,并且切断路径的代价之和最小。 小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。现在他对每条单向道路提出两个问题: 问题一:是否存在一个最小代价路径切断方案,其中该道路被切断? 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断? 现在请你回答这两个问题。

Input

第一行有4个正整数,依次为N,M,s和t。第2行到第(M+1)行每行3个正 整数v,u,c表示v中转站到u中转站之间有单向道路相连,单向道路的起点是v, 终点是u,切断它的代价是c(1≤c≤100000)。 注意:两个中转站之间可能有多条道路直接相连。 同一行相邻两数之间可能有一个或多个空格。

Output

对每条单向边,按输入顺序,依次输出一行,包含两个非0即1的整数,分 别表示对问题一和问题二的回答(其中输出1表示是,输出0表示否)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。


题意和思路:

1。题目直接说了跑最大流,所以第一步就直接用板子跑最大流(最小割)就行了,我最大流的板子在这里
2。虽然你求出来了最小割答案,但是对于这个答案经过了多少条不同路径的还不清楚!不过我们知道每条边流量flow的变化量和它反向边流量flow的边化量。这点很重要。
3。知道了跑完最大流之后的流量,你就直接在这个残量网络上用tarjan缩点
4。遍历每条边(不遍历反向边)。下面分情况讨论:

1.如果其反向边没有流量变化,代表最大流没有经过这条边,直接输出0 0;
2.如果这条边的两个端点缩点后在一个强连通分量中,就是说缩点后为同一点编号,那么也输出0 0;
3.如果两个端点缩点后,一个和S点在同一强连通分量另一个和T点在同一强连通分量,则输出1 1;反之输出1 0;


AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#define fuck(x) printf("*%d\n",x )
using namespace std;
typedef long long LL;
const int MAXN = 5005;
const int N = 5005;
const int X = 0x7fffffff;
const int INF = 0x3f3f3f3f ;
char s[MAXN];
struct lp{
    int u,v, next;
    LL cap;
} cw[MAXN * 100],temp[MAXN*100];
int n, m, vs, vt, NE, NV;
int head[MAXN],pre[MAXN],cur[MAXN],level[MAXN],gap[MAXN];
int vis[N],Index;
int dfn[N],low[N];
int qltNum;
int qltMap[N];
stack<int> st;
inline void Insert(int u, int v, LL cap)
{
    cw[NE].v = v;
    cw[NE].cap = cap;
    cw[NE].next = head[u];
    cw[NE].u=u;
    head[u] = NE++;
    cur[u] = head[u];
    cw[NE].v = u;
    cw[NE].u=v;
    cw[NE].cap = 0;
    cw[NE].next = head[v];
    head[v] = NE++;
    cur[v] = head[v];
}
LL SAP(int vs,int vt){
    memset(level,0,sizeof(level));
    memset(pre,-1,sizeof(pre));
    memset(gap,0,sizeof(gap));
    int u=pre[vs]=vs;
    LL maxflow=0,aug=-1;
    gap[0]=NV;
    while(level[vs]<NV){
        int flag=0;
        for(int &i=cur[u];i!=-1;i=cw[i].next){
            int v=cw[i].v;
            if(cw[i].cap&&level[u]==level[v]+1){
                aug==-1?(aug=cw[i].cap):(aug=min(aug,cw[i].cap));
                pre[v]=u;
                u=v;
                if(v==vt){
                    maxflow+=aug;
                    for(u=pre[v];v!=vs;v=u,u=pre[u]){
                        cw[cur[u]].cap-=aug;
                        cw[cur[u]^1].cap+=aug;
                    }
                    aug=-1;
                }
                flag=1;
                if(flag)break;
            }
        }
        if(flag)continue;
        int minlevel=NV;
        for(int i=head[u];i!=-1;i=cw[i].next){
            int v=cw[i].v;
            if(cw[i].cap&&minlevel>level[v]){
                cur[u]=i;
                minlevel=level[v];
            }
        }
        if((--gap[level[u]])==0)break;
        level[u]=minlevel+1;
        gap[level[u]]++;
        u=pre[u];
    }
    return maxflow;
} 
void dfs(int u,int fa){
    dfn[u] = low[u] = ++Index;
    vis[u]=1;int v;
    st.push(u);
    for(int i=head[u];~i;i=cw[i].next){
        v=cw[i].v;
        if(cw[i].cap<=0)continue;
        if(!vis[v]){
            dfs(v,u);
            low[u]=min(low[u],low[v]);
        }else if(vis[v]==1){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){
        qltNum++;
        do{
            v=st.top();st.pop();
            vis[v]=2;
            qltMap[v]=qltNum;
        }while(v!=u);
    }
}
void tarjan(){
    for(int i=1;i<=n;++i){
        if(!vis[i]){
            dfs(i,0);
        }
    }
}
void init(){
    qltNum=0;while(!st.empty())st.pop();
    Index=0;memset(vis,0,sizeof(vis));
    memset(qltMap,0,sizeof(qltMap));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
}
#define DEBUG
int main()
{
#ifdef DEBUG
    freopen("D:in.in", "r", stdin);
    freopen("D:out.out", "w", stdout);  
#endif
    int S,T;
    while(~scanf("%d%d%d%d",&n,&m,&S,&T)) {
        //n n
        NV=n;NE=0;
        vs=S,vt=T;
        memset(head, -1, sizeof(head));
        memset(cur, -1, sizeof(cur));
        for(int i=0;i<m;++i){
            int x,y;
            LL z;
            scanf("%d%d%lld",&x,&y,&z);
            Insert(x,y,z);
        }
        LL ans =SAP(vs,vt);
        init();
        tarjan();
        int qltS=qltMap[S],qltT=qltMap[T];
        for(int i=0;i<NE;i+=2){
            if(cw[i^1].cap>0){
                if(qltMap[cw[i].u]!=qltMap[cw[i].v]){
                    if((qltMap[cw[i].u]==qltS&&qltMap[cw[i].v]==qltT)||(qltMap[cw[i].u]==qltT&&qltMap[cw[i].v]==qltS)){
                        printf("1 1\n");
                    }else printf("1 0\n");
                }else{
                    printf("0 0\n");
                }
            }else printf("0 0\n");
        }

    }
    return 0;
}
/*
1 0
1 0
0 0
1 0
0 0
1 0
1 0
最小割唯一性判定

jcvb:

在残余网络上跑tarjan求出所有SCC,记id[u]为点u所在SCC的编号。显然有id[s]!=id[t](否则s到t有通路,能继续增广)。

①对于任意一条满流边(u,v),(u,v)能够出现在某个最小割集中,当且仅当id[u]!=id[v];
②对于任意一条满流边(u,v),(u,v)必定出现在最小割集中,当且仅当id[u]==id[s]且id[v]==id[t]。

①
<==将每个SCC缩成一个点,得到的新图就只含有满流边了。那么新图的任一s-t割都对应原图的某个最小割,从中任取一个把id[u]和id[v]割开的割即可证明。
②
<==:假设将(u,v)的边权增大,那么残余网络中会出现s->u->v->t的通路,从而能继续增广,于是最大流流量(也就是最小割容量)会增大。这即说明(u,v)是最小割集中必须出现的边。
*/

猜你喜欢

转载自blog.csdn.net/qq_39599067/article/details/80157368