「BZOJ 3511」土地划分

这题好简单好简单呀!不难发现,这是一个最小割模型。

首先记一个 s u m = i = 2 N 1 A i + B i

所以 a n s = s u m m i n C u t ,我们需要做的就是最小化 m i n C u t (最小割),来最大化 a n s (答案)。

这里写图片描述

简化一下最小割模型(如上图): S 是源, T 是汇, x , y 是任意两条有公路的城市。

若割去与 S 相连的边则表示划分给城市 A ,若割去与 T 相连的边则表示划分给城市 B

有如下四种割法:

  1. a + b ,即 x y 都划分为 A ,那么 m i n C u t = V B x + V B y E A ,即减去划分给 B 的第 1 类得分,加上公路连接第 2 类得分;

  2. c + d ,即 x y 都划分为 B ,那么 m i n C u t = V A x + V A y E B ,即减去划分给 A 的第 1 类得分,加上公路连接第 2 类得分;

  3. a + f + d ,即 x 划分为 A y 划分为 B ,那么 m i n C u t = V B x + V A y + E C ,即减去 x 划分给 B y 划分为 A 的第 1 类得分,再减去公路连接第 2 类得分;

  4. a + f + d ,即 x 划分为 B y 划分为 A ,那么 m i n C u t = V A x + V B y + E C ,即减去 x 划分给 A y 划分为 B 的第 1 类得分,再减去公路连接第 2 类得分;

于是我们可以通过解上述的方程,得出:

a = V B x , b = V B y E A , c = V A x , d = V A y E B , e = E C + E A , f = E C + E B

于是用这些方程的解作为边的容量即可,由于容量不能为负,所以你可以将连接 S T 的边同时加上一个数,最后给答案加回这些数即可。

由于城市 1 和城市 N 已经划分好了,所以你需要稍加变化,例如城市 1 你可以将其与 T 连接的边的容量设为 ,来强制其属于 A

#include <cstdio>
#include <cstring>
#define Min(_A, _B) (_A < _B ? _A : _B)
#define Max(_A, _B) (_A > _B ? _A : _B)
#define Abs(_A) (_A > 0 ? _A : -(_A))
#define R register
int F()
{
    R int x = 0; R char ch; R bool minus = 0;
    while(ch = getchar(), (ch < '0' || ch > '9') && ch != '-');
    ch == '-' ? minus = 1 : x = ch - '0';
    while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - '0';
    return minus ? -x : x;
}

int N, M;
const int Inf = 1e9, MaxN = 10010, MaxM = 100010;
int Point[MaxN], Next[MaxM << 1], To[MaxM << 1], C[MaxM << 1], q = 1, S, T, Cur[MaxN];
void Add(R int u, R int v, R int c)
{
    Next[++q] = Point[u]; Point[u] = q; To[q] = v; C[q] = c;
    Next[++q] = Point[v]; Point[v] = q; To[q] = u; C[q] = 0;
}
int Lv[MaxN], Q[MaxN];
bool BFS()
{
    memset(Lv, -1, sizeof(Lv));
    R int *head = Q, *tail = Q;
    Lv[*(++tail) = T] = 1;
    do
    {
        ++head;
        for(R int j = Point[*head]; j; j = Next[j])
            if(Lv[To[j]] == -1 && C[j ^ 1])
            {
                Lv[To[j]] = Lv[*head] + 1;
                if(To[j] == S) return 1;
                *(++tail) = To[j];
            }
    }
    while(head < tail);
    return 0;
}
int DFS(R int now, R int Flow)
{
    if(now == T) return Flow;
    R int tmp, f = Flow;
    for(R int &j = Cur[now]; j; j = Next[j])
        if(Lv[To[j]] + 1 == Lv[now] && C[j] && (tmp = DFS(To[j], Min(f, C[j]))))
        {
            f -= tmp;
            C[j] -= tmp;
            C[j ^ 1] += tmp;
            if(f == 0) return Flow;
        }
    return Flow - f;
}
int dinic()
{
    R int Ans = 0;
    while(BFS())
    {
        for(R int i = 1; i <= T; ++i) Cur[i] = Point[i];
        Ans += DFS(S, Inf);
    }
    return Ans;
}
int A[MaxN], B[MaxN];
int main()
{
    scanf("%d %d", &N, &M); R int Ans = 0;
    T = (S = N + 1) + 1;
    A[1] = Inf; B[1] = 0; A[N] = 0; B[N] = Inf;
    for(R int i = 2; i < N; ++i) Ans += (A[i] = F()); 
    for(R int i = 2; i < N; ++i) Ans += (B[i] = F());
    for(R int i = 1; i <= M; ++i)
    {
        R int x = F(), y = F(), EA = F(), EB = F(), EC = F();
        A[y] -= EB; B[y] -= EA;
        Add(x, y, EC + EA); Add(y, x, EC + EB);
    }
    R int lim = 0;
    for(R int i = 1; i <= N; ++i) lim = Min(Min(A[i], B[i]), lim);
    lim = Abs(lim);
    Ans += N * lim;
    for(R int i = 1; i <= N; ++i)
    {
        Add(S, i, B[i] + lim);
        Add(i, T, A[i] + lim);
    }
    printf("%d\n", Ans - dinic());
    return 0;
}

猜你喜欢

转载自blog.csdn.net/steaunk/article/details/79995892