这题好简单好简单呀!不难发现,这是一个最小割模型。
首先记一个 sum s u m = ∑N−1i=2Ai+Bi ∑ i = 2 N − 1 A i + B i ,
所以 ans=sum−minCut 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 是源, 是汇, x,y x , y 是任意两条有公路的城市。
若割去与 S S 相连的边则表示划分给城市 ,若割去与 T T 相连的边则表示划分给城市 。
有如下四种割法:
a+b a + b ,即 x x 与 都划分为 A A ,那么 ,即减去划分给 B B 的第 类得分,加上公路连接第 2 2 类得分;
,即 x x 与 都划分为 B B ,那么 ,即减去划分给 A A 的第 类得分,加上公路连接第 2 2 类得分;
,即 x x 划分为 , y y 划分为 ,那么 minCut=VBx+VAy+EC m i n C u t = V B x + V A y + E C ,即减去 x x 划分给 和 y y 划分为 的第 1 1 类得分,再减去公路连接第 类得分;
a+f+d a + f + d ,即 x x 划分为 , y y 划分为 ,那么 minCut=VAx+VBy+EC m i n C u t = V A x + V B y + E C ,即减去 x x 划分给 和 y y 划分为 的第 1 1 类得分,再减去公路连接第 类得分;
于是我们可以通过解上述的方程,得出:
a=VBx,b=VBy−EA,c=VAx,d=VAy−EB,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 和 的边同时加上一个数,最后给答案加回这些数即可。
由于城市 1 1 和城市 已经划分好了,所以你需要稍加变化,例如城市 1 1 你可以将其与 连接的边的容量设为 ∞ ∞ ,来强制其属于 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;
}