P2944 [USACO09MAR]地震损失2Earthquake Damage 2(网络流)

P2944 [USACO09MAR]地震损失2Earthquake Damage 2

$P$个点,$C$条双向边。求最少删去几个点使$N$个给定的点与点$1$分开。

显然的最小割。

将点$i$套路地拆成$i_1,i_2$,割点转化成割边

对于点$1$:$link(S,1_1,inf),link(1_1,1_2,inf)$。保证不被割掉,且分到$S$割中

对于每个给定点$k$:$link(k_2,T,inf),link(k_1,k_2,inf)$。保证不被割掉,且分到$T$割中

对于每条双向边$(u,v)$:$link(u_1,v_2,inf),link(u_2,v_1,inf)$。保证不被割掉

对于剩余点:$link(i_1,i_2,1)$。可能被割掉

建好图跑遍最大流(最小割)就好辣

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define N 10005
#define M 100005
const int inf=2e9;
int n,S,T,d[N],cur[N];
queue <int> h; bool vis[N],e[N];
int cnt=1,hd[N],nxt[M],ed[N],poi[M],val[M];
inline void adde(int x,int y,int v){
    nxt[ed[x]]=++cnt, hd[x]=hd[x]?hd[x]:cnt,
    ed[x]=cnt, poi[cnt]=y, val[cnt]=v;
}
inline void link(int x,int y,int v){adde(x,y,v),adde(y,x,0);}
bool bfs(){
    for(int i=1;i<=T;++i) vis[i]=0,cur[i]=hd[i];
    h.push(S); vis[S]=1;
    while(!h.empty()){
        int x=h.front(); h.pop();
        for(int i=hd[x];i;i=nxt[i]){
            int to=poi[i];
            if(!vis[to]&&val[i]>0)
                vis[to]=1,d[to]=d[x]+1,h.push(to);
        }
    }return vis[T];
}
int dfs(int x,int a){
    if(x==T||a==0) return a;
    int F=0,f;
    for(int &i=cur[x];i;i=nxt[i]){
        int to=poi[i];
        if(d[to]==d[x]+1&&(f=dfs(to,min(a,val[i])))>0)
            a-=f,F+=f,val[i]-=f,val[i^1]+=f;
        if(!a) break;
    }return F;
}
int dinic(){int re=0; while(bfs())re+=dfs(S,inf); return re;}
int main(){
    int m,c,q1,q2;
    scanf("%d%d%d",&n,&m,&c);
    S=n*2+1; T=S+1;
    link(S,1,inf); link(1,1+n,inf);
    while(m--){
        scanf("%d%d",&q1,&q2);
        link(q1+n,q2,inf);
        link(q2+n,q1,inf);
    }
    while(c--){
        scanf("%d",&q1); e[q1]=1;
        link(q1,q1+n,inf);
        link(q1+n,T,inf);
    }
    for(int i=1;i<=n;++i)
        if(!e[i]) link(i,i+n,1);
    printf("%d",dinic());
    return 0;
}

 

posted @ 2019-05-04 13:58  kafuuchino  阅读(163)  评论(0编辑  收藏  举报