BZOJ2893: 征服王

BZOJ2893: 征服王

费用流 的 奇妙世界

题解:

哇塞好神奇!
♪(´∇`*)
竟然还有如此厚颜无耻**巧妙**的费用流!

好啦以下是抄的题解:

首先缩点,变成DAG
“见到有向图60%是缩点”——某大佬

然后就是一个DAG上的一条边可以重复走最小路径覆盖。
DAG的最小路径覆盖可以用二分图匹配来解决。

可重复的就要用费用流了:
对于一个点u,拆成u->u’ ,连两条边,一条cap=1 cost=1,另一条cap=inf cost=0
s->u cap=inf cost=0,u’ -> t cap=inf cost=0
对于原图的边(u,v): u’->v cap=inf cost=0
跑最大费用流,每次增广1的流量,增广次数即为答案。
有cost的边代表这个点经过一次,那么最大费用流每次会尽量走费用多的边,即走尽量多的点,正确性由网络流保证。

复杂度?
每次增广至少有1个cost,总共有n个cost,最坏增广n次。

对了,前面一个namespace里怎么使用后面namespace的变量啊。。。语言渣。。。
你会看到为了在MXF中用scc我把它放在全局了。。。

Code:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <stack>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
using namespace std;
const int N = 2017;
const int M = 20010;
const int INF = 0x3f3f3f3f;

int n,m,A[N],B[N],scc;

namespace MXF{
    struct Edge{
        int from,to,nxt,cap,flow,cost;
    } e[200005];
    int head[N], ec=1;
    void add(int a,int b,int cap,int cost){
        ec++; e[ec].cap=cap; e[ec].flow=0; e[ec].cost=cost;
        e[ec].from=a; e[ec].to=b; e[ec].nxt=head[a]; head[a]=ec;
    }
    void add2(int a,int b,int cap,int cost){
//      D(a); D(b); D(cap); D(cost); E;
        add(a,b,cap,cost); add(b,a,0,-cost);
    }

    int S,T,SS;

    void init(){ 
        ec=1; memset(head,0,sizeof(head)); 
        S=N-7; T=S+1; SS=S+2;
    }

    int dis[N],pre[N]; bool vis[N];
    bool spfa(){
        memset(dis,0x3f,sizeof(dis));
        memset(vis,false,sizeof(vis));
        queue<int> q; q.push(S); vis[S]=true; dis[S]=0; pre[S]=0;
        while(!q.empty()){
            int u=q.front(); q.pop(); vis[u]=false;
            for(int i=head[u];i;i=e[i].nxt){
                int v=e[i].to;
                if(e[i].cap>e[i].flow && dis[v]>dis[u]+e[i].cost){
                    dis[v]=dis[u]+e[i].cost; pre[v]=i;
                    if(!vis[v]){ vis[v]=true; q.push(v); }          
                }
            }
        }
        return dis[T]!=INF;
    }

    void solve(){
        int cost=0, last, a, cnt=0;
        add2(S,SS,1,0);
        while(spfa()){
            a=INF; cnt++; last=cost;
            for(int i=pre[T];i;i=pre[e[i].from]){ a=min(a,e[i].cap-e[i].flow); }
            for(int i=pre[T];i;i=pre[e[i].from]){ e[i].flow+=a; e[i^1].flow-=a; cost+=a*e[i].cost; }
//          D(a); D(cost); E;
            if(cost==last) break;
            add2(S,SS,1,0);
        }
        if(-cost==scc) printf("%d\n",cnt-1);
        else puts("no solution");
    }
}

namespace TARJAN{
    struct Edge{ int to,next; } e[M];
    int head[N], ec=0;
    void add(int a,int b){
        ec++; e[ec].to=b; e[ec].next=head[a]; head[a]=ec;
    }

    int low[N],dfn[N],belong[N],tim; bool inq[N];
    stack<int> sta;
    void init(){ 
        ec=0; memset(head,0,sizeof(head));
        scc=0; tim=0; while(!sta.empty()) sta.pop();
        memset(dfn,0,sizeof(dfn)); memset(inq,false,sizeof(inq)); 
    }

    void tarjan(int u){
//      D(u); E;
        low[u]=dfn[u]=++tim;
        sta.push(u); inq[u]=true;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(!dfn[v]){
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(inq[v]){
                low[u]=min(low[u],dfn[v]);
            }
        }
        if(low[u]==dfn[u]){
            int x=0; scc++;
            while(x!=u){
                x=sta.top(); sta.pop(); inq[x]=false;
                belong[x]=scc;
            }
        }
    } 
    void tarjan(){ for(int i=1;i<=n;i++){ if(!dfn[i])tarjan(i); } }

    void work(){
        for(int u=1;u<=n;u++){
            for(int i=head[u];i;i=e[i].next){
                int v=e[i].to;
                if(belong[u]!=belong[v])
                    MXF::add2(belong[u]+scc,belong[v],INF,0);
            }
        }
        for(int i=1;i<=scc;i++){
            MXF::add2(i,i+scc,1,-1); 
            MXF::add2(i,i+scc,INF,0);
        }
        for(int i=1;i<=A[0];i++) MXF::add2(MXF::SS,belong[A[i]],INF,0);
        for(int i=1;i<=B[0];i++) MXF::add2(belong[B[i]]+scc,MXF::T,INF,0);
    }
}

void input(){
    cin>>n>>m>>A[0]>>B[0];
    MXF::init();
    TARJAN::init();
    for(int i=1;i<=A[0];i++) scanf("%d",A+i);
    for(int i=1;i<=B[0];i++) scanf("%d",B+i);
    int u,v;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        TARJAN::add(u,v);
    }
}

int main(){
    freopen("a.in","r",stdin);
    int cas; cin>>cas;
    while(cas--){
        input();
        TARJAN::tarjan();
//      D(scc); E;
//      for(int i=1;i<=n;i++) 
//          cout<<TARJAN::belong[i]<<endl; 
        TARJAN::work();
        MXF::solve();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值