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;
}