洛谷2805 [NOI2009]植物大战僵尸 (拓扑排序+最小割)
阅读原文时间:2023年07月08日阅读:3

坚决抵制长题面的题目!

首先观察到这个题目中,我们会发现,我们对于原图中的保护关系(一个点右边的点对于这个点也算是保护)

相当于一种依赖。

那么不难看出这个题实际上是一个最大权闭合子图模型。

我们直接对于权值为负数的边,\(S\rightarrow now\),流量是\(-a[i][j]\),表示打掉他要花这么多的代价。

对于权值为正的边,\(now \rightarrow T\) ,流量是\(a[i][j]\),表示如果割掉这个边,表示放弃他的收益。

(这里之所以\(S和T\)不能反过来,因为我们跑最小割的时候,是要保证S到T不连通,所以你要让负的权值与S相连,才会让依赖关系有意义)

对于每个点,向他保护的点连边,边权为\(inf\)。表示这个关系不能打破。

同时一个点右边的点,向这个点连边,流量也是\(inf\),因为右边的点要比左边的点先被打。

最后的答案就是\(正权值的sum - 最小割\)(总的收益,减去花费和舍去的。)

那么建出来图,我们会发现其实这个是有问题的。

因为可能存在环的情况。

(\(A保护B,B保护A\))

那么应该怎么办呢?

我们发现如果存在一个环,那么环能保护到的点,以及再往后的点,都是无敌的!

所以合法的点,就是从起点开始,找到所有的满足起点到这个点的所有路径都不经过环的 点。

那么这个可以通过拓扑排序来实现。

qwq

只需要一开始先把所有的依赖关系,跑一遍拓扑排序。

然后选出来有效的点,再建图就ok

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define pb push_back
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 3010;
const int maxm = 2e6+1e2;
const int inf = 1e9;
int n,m,cnt=1;
int point[maxn],nxt[maxm],to[maxm],val[maxm];
int h[maxn],in[maxn];
int s,t;
int a[maxn][maxn];
int sum;
int num;
int dfn[maxn];
int tag[maxn];
vector<int> v[maxn];
void init()
{
    cnt=1;
    memset(point,0,sizeof(point));
    memset(in,0,sizeof(in));
}
void add(int x,int y)
{
    nxt[++cnt]=point[x];
    to[cnt]=y;
    point[x]=cnt;
    in[y]++;
}
void addedge(int x,int y,int w)
{
    nxt[++cnt]=point[x];
    to[cnt]=y;
    val[cnt]=w;
    point[x]=cnt;
}
void insert(int x,int y,int w)
{
    addedge(x,y,w);
    addedge(y,x,0);
}
queue<int> q;
bool bfs(int s)
{
    memset(h,-1,sizeof(h));
    h[s]=0;
    q.push(s);
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        for (int i=point[x];i;i=nxt[i])
        {
            int p=to[i];
            if (h[p]==-1 && val[i]>0)
            {
                h[p]=h[x]+1;
                q.push(p);
            }
        }
    }
    if (h[t]==-1) return false;
    return true;
}
int dfs(int x,int low)
{
    if (x==t || low==0) return low;
    int totflow=0;
    for (int i=point[x];i;i=nxt[i])
    {
        int p = to[i];
        if (h[p]==h[x]+1 && val[i]>0)
        {
            int tmp = dfs(p,min(low,val[i]));
            val[i]-=tmp;
            val[i^1]+=tmp;
            low-=tmp;
            totflow+=tmp;
            if (low==0) return totflow;
        }
    }
    if (low>0) h[x]=-1;
    return totflow;
}
int dinic()
{
    int ans=0;
    while (bfs(s))
    {
        ans=ans+dfs(s,inf);
    }
    return ans;
}
int getnum(int x,int y)
{
    return (x-1)*m+y;
}
void tpsort()
{
    while (!q.empty()) q.pop();
    for (int i=1;i<=num;i++)
    {
        if (!in[i]) q.push(i),tag[i]=1,dfn[i]=1;
    }
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        for (int i=point[x];i;i=nxt[i])
        {
            int p=to[i];
            in[p]--;
            if (!in[p])
            {
                dfn[p]=dfn[x]+1;
                q.push(p);
                tag[p]=1;
            }
        }
    }
}
int main()
{
  n=read(),m=read();
  num=n*m;
  for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    {
        a[i][j]=read();
        int num = read();
        for (int k=1;k<=num;k++)
        {
            int x=read(),y=read();
            x++,y++;
            v[getnum(i,j)].pb(getnum(x,y));
            add(getnum(i,j),getnum(x,y));
        }
        if (j!=m) add(getnum(i,j+1),getnum(i,j));
    }
  tpsort();
  init();
  s=maxn-10;
  t=s+1;
  for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    {
        int now=getnum(i,j);
        if(!tag[now]) continue;
        if(a[i][j]>0) sum+=a[i][j];
        for (int k=0;k<v[now].size();k++)
          if (tag[v[now][k]]) insert(now,v[now][k],inf);
        if (a[i][j]>0) insert(now,t,a[i][j]);
        else insert(s,now,-a[i][j]);
    }
  for (int i=1;i<=n;i++)
  {
       for (int j=1;j<=m;j++)
       {
          int now = getnum(i,j);
          int ri  = getnum(i,j+1);
          if (j==m) continue;
          if (!tag[now] || !tag[ri]) continue;
          if (dfn[ri]>dfn[now]) swap(now,ri);
          insert(ri,now,inf);
     }
  }
  cout<<sum-dinic();
  return 0;
}