「BZOJ 3511」土地划分

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

首先记一个 sum s u m = N1i=2Ai+Bi ∑ i = 2 N − 1 A i + B i

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

这里写图片描述

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

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

有如下四种割法:

  1. a+b a + b ,即 x x y 都划分为 A A ,那么 minCut=VBx+VByEA,即减去划分给 B B 的第 1 类得分,加上公路连接第 2 2 类得分;

  2. c+d,即 x x y 都划分为 B B ,那么 minCut=VAx+VAyEB,即减去划分给 A A 的第 1 类得分,加上公路连接第 2 2 类得分;

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

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

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

a=VBx,b=VByEA,c=VAx,d=VAyEB,e=EC+EA,f=EC+EB 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 S T 的边同时加上一个数,最后给答案加回这些数即可。

由于城市 1 1 和城市 N 已经划分好了,所以你需要稍加变化,例如城市 1 1 你可以将其与 T 连接的边的容量设为 ,来强制其属于 A A <script type="math/tex" id="MathJax-Element-307">A</script>。

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值