BZOJ 1565 [NOI2009]植物大战僵尸 | 网络流

传送门

BZOJ 1565

题解

这道题也是个经典的最大权闭合子图……

复习一下最大权闭合子图是什么?
就是一个DAG上,每个点有个或正或负的点权,有的点依赖于另外一些点(如果选这个点,则被依赖点必选),问选出一些点的权值和最大是多少。

这个问题怎么解决?
网络流建图,被依赖点向依赖点连INF的边,若某点权为正则源点向它连相应容量的边,否则它向汇点连点权的绝对值容量的边。

问题是……这道题是有环的……
有环也没关系,按照题意,环上的点都不能选,那么直接让环上的所有点向汇点连INF边即可。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;
typedef long long ll;
#define enter putchar('\n')
#define space putchar(' ')
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c > '9' || c < '0')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 605, M = 1000005, INF = 0x3f3f3f3f;
int n, m, src, des, ans;
int _adj[N], _nxt[M], _go[M], _ecnt;
#define id(x, y) (x * m + y + 1)
int ecnt = 1, adj[N], cur[N], dis[N], nxt[M], go[M], cap[M];
int low[N], dfn[N], idx, stk[N], top;
bool ins[N], mark[N];

void ADD(int u, int v, int w){
    go[++ecnt] = v;
    nxt[ecnt] = adj[u];
    adj[u] = ecnt;
    cap[ecnt] = w;
}
void add(int u, int v, int w){
    ADD(u, v, w);
    ADD(v, u, 0);
}
void _add(int u, int v){
    _go[++_ecnt] = v;
    _nxt[_ecnt] = _adj[u];
    _adj[u] = _ecnt;
}
bool bfs(){
    static int que[N], qr;
    for(int i = 1; i <= des; i++)
        cur[i] = adj[i], dis[i] = -1;
    dis[src] = 0, que[qr = 1] = src;
    for(int ql = 1; ql <= qr; ql++)
        for(int u = que[ql], e = adj[u], v; e; e = nxt[e])
            if(cap[e] && dis[v = go[e]] == -1)
                dis[v] = dis[u] + 1, que[++qr] = v;
    return dis[des] != -1;
}
int dfs(int u, int flow){
    if(u == des) return flow;
    int ret = 0;
    for(int &e = cur[u], v; e; e = nxt[e])
        if(cap[e] && dis[v = go[e]] == dis[u] + 1){
            int delta = dfs(v, min(cap[e], flow - ret));
            if(delta){
                cap[e] -= delta;
                cap[e ^ 1] += delta;
                ret += delta;
                if(ret == flow) return ret;
            }
        }
    dis[u] = -1;
    return ret;
}
int maxflow(){
    int ret = 0;
    while(bfs()) ret += dfs(src, INF);
    return ret;
}
void tarjan(int u){
    stk[++top] = u, ins[u] = 1;
    low[u] = dfn[u] = ++idx;
    for(int e = _adj[u], v; e; e = _nxt[e])
        if(v = _go[e], !dfn[v])
            tarjan(v), low[u] = min(low[u], low[v]);
        else if(ins[v])
            low[u] = min(low[u], dfn[v]);
    if(low[u] == dfn[u]){
        int v = stk[top];
        if(v == u) top--, ins[u] = 0;
        else
            while(v != u){
                ins[v = stk[top--]] = 0;
                mark[v] = 1;
            }
    }
}

int main(){
    read(n), read(m), src = n * m + 1, des = n * m + 2;
    for(int i = 0, k, w, x, y, num = 1; i < n; i++)
        for(int j = 0; j < m; j++, num++){
            if(j) _add(num - 1, num), add(num - 1, num, INF);
            read(w), read(k);
            if(w >= 0) add(src, num, w), ans += w;
            else add(num, des, -w);
            while(k--){
                read(x), read(y);
                _add(id(x, y), num);
                add(id(x, y), num, INF);
            }
        }
    for(int i = 1; i <= n * m; i++)
        if(!dfn[i]) tarjan(i);
    for(int i = 1; i <= n * m; i++)
        if(mark[i]) add(i, des, INF);
    write(ans - maxflow()), enter;

    return 0;
}
posted @ 2018-01-19 15:15  胡小兔  阅读(306)  评论(0编辑  收藏  举报