NOI2009 植物大战僵尸

题目链接:戳我

最大权闭合子图。一般来说建图方法都是——正权值连S,负权值连T,如果一个节点被选择,其子节点也必须被选择,连一条INF的有向边。求最小割即可,答案就是正权点权值和-最大流——中间的边一定不可能被割掉左边割掉,左边割掉表示不选择,右边割掉表示选择。

但是有可能出现环的情况,这样子不能直接割。我们考虑先来一个tarjan缩点,给每个点的权值都赋成-inf。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#define S 0
#define T n*m+1
#define MAXN 400010
#define inf 0x3f3f3f3f
#define check
using namespace std;

int n,m,cnt1=1,cnt2,cnt,top,ans,tim;
int head1[MAXN],head2[MAXN],sum[MAXN],dep[MAXN],cur[MAXN];
int in[MAXN],dfn[MAXN],low[MAXN],ins[MAXN],st[MAXN];
struct Edge{int nxt,to,dis;}edge[MAXN],pre[MAXN];

inline int id(int x,int y){return m*(x-1)+y;}

inline void add1(int from,int to,int dis,int p)
{
    edge[++cnt1].nxt=head1[from],edge[cnt1].to=to,edge[cnt1].dis=dis,head1[from]=cnt1;
    edge[++cnt1].nxt=head1[to],edge[cnt1].to=from,edge[cnt1].dis=0,head1[to]=cnt1;
}
inline void add2(int from,int to){pre[++cnt2].nxt=head2[from],pre[cnt2].to=to,head2[from]=cnt2;}

inline void tarjan(int x)
{
    dfn[x]=low[x]=++cnt;
    st[++top]=x;in[x]=1;
    for(int i=head2[x];i;i=pre[i].nxt)
    {
        int v=pre[i].to;
        if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
        else if(in[v]) low[x]=min(low[x],dfn[v]);
    }
    if(dfn[x]==low[x])
    {
        if(st[top]==x)--top,in[x]=0;
        else
        {
            int v;
            do{v=st[top];top--;in[v]=0;sum[v]=-inf;}while(x!=v);
        }
    }
}

inline bool bfs()
{
    queue<int>q;
    memset(dep,0x3f,sizeof(dep));
    memcpy(cur,head1,sizeof(cur));
    q.push(S); dep[S]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head1[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(dep[v]==0x3f3f3f3f&&edge[i].dis)
                dep[v]=dep[u]+1,q.push(v); 
        }
    }
    if(dep[T]==0x3f3f3f3f) return false;
    return true;
}
inline int dfs(int x,int f)
{
    if(!f||x==T) return f;
    int used=0,w;
    for(int i=cur[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        cur[x]=i;
        if(dep[v]==dep[x]+1&&(w=dfs(v,min(f,edge[i].dis))))
        {
            edge[i].dis-=w,edge[i^1].dis+=w;
            used+=w,f-=w;
            if(!f) break;
        }
    }
    return used;
}

inline int dinic()
{
    int cur_ans=0;
    while(bfs()) cur_ans+=dfs(S,inf);
    return cur_ans;
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            int cc;
            if(j<m) add2(id(i,j),id(i,j+1));//printf("[%d %d]\n",id(i,j),id(i,j+1));
            scanf("%d%d",&sum[id(i,j)],&cc);
            for(int k=1;k<=cc;k++)
            {
                int cur_x,cur_y;
                scanf("%d%d",&cur_x,&cur_y);cur_x++,cur_y++;
                add2(id(cur_x,cur_y),id(i,j));//printf("[%d,%d]\n",id(cur_x,cur_y),id(i,j));
            }
        }
    }
    for(int i=1;i<T;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(sum[id(i,j)]>0) 
            {
                ans+=sum[id(i,j)],add1(S,id(i,j),sum[id(i,j)],1);
                //printf("1[%d,%d] %d\n",S,id(i,j),sum[id(i,j)]);
            }
            else 
            {   
                add1(id(i,j),T,-sum[id(i,j)],2);
                //printf("2[%d,%d] %d\n",id(i,j),T,-sum[id(i,j)]);
            }
            for(int k=head2[id(i,j)];k;k=pre[k].nxt)
            {
                add1(id(i,j),pre[k].to,inf,3);
                //printf("3[%d,%d] %d\n",id(i,j),pre[k].to,inf);
            }
        }
    }
    printf("%d\n",ans-dinic());
    return 0;
}
posted @ 2019-02-12 10:13  风浔凌  阅读(170)  评论(0编辑  收藏  举报