Description:
1<=n<=50
题解:
我的网络流学的怎么这么菜啊,GDOI时也是网络流的题一分没有。
好吧看到n<=50时的时候我就根本没有想过这是个流,就想着乱搞去了。
首先确定一个O(2n∗n2)O(2^n*n^2)O(2n∗n2)的做法,就是我们枚举一些点,判断它们是否能够作为turpo序一个区间。
判断条件也比较好想:不存在i,j,k,使i,j∈S,k∉S,且i能通过k到j不存在i,j,k,使i,j∈S,k∉S,且i能通过k到j不存在i,j,k,使i,j∈S,k∈/S,且i能通过k到j
也就是对一条路径上的点,一定是先不选,然后选,然后不选。
对每一个点xxx拆成x1,x2x1,x2x1,x2
考虑连边S−>x1−>x2−>TS->x1->x2->TS−>x1−>x2−>T
割那条边表示分到哪段。
注意若原图有边x−>yx->yx−>y,则x选的段<=y选的段。
所以x1−>y1,x2−>y2,r=∞x1->y1,x2->y2,r=∞x1−>y1,x2−>y2,r=∞
然后就是权值有正有负,那么可以先假设选了所有的正权点,那么S−>x1,x2−>TS->x1,x2->TS−>x1,x2−>T的r=a[x]r=a[x]r=a[x]
假设不选负权点,x1−>x2,r=−a[x]x1->x2,r=-a[x]x1−>x2,r=−a[x]
Ans=∑a[x]>0a[x]−最小割Ans=\sum_{a[x]>0}a[x]-最小割Ans=∑a[x]>0a[x]−最小割
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i < B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
#define ul unsigned long long
using namespace std;
const int N = 1e5 + 5;
int fi[N], nt[N], to[N], r[N], tot = 1;
void link(int x, int y, int z) {
nt[++ tot] = fi[x], to[tot] = y, r[tot] = z, fi[x] = tot;
nt[++ tot] = fi[y], to[tot] = x, r[tot] = 0, fi[y] = tot;
}
int n, m, a[51], x, y;
int S, T, co[N], d[N], cur[N], ans;
int dg(int x, int flow) {
if(x == T) return flow;
int use = 0;
for(int i = cur[x]; i; cur[x] = i = nt[i])
if(d[x] == d[to[i]] + 1 && r[i]) {
int tmp = dg(to[i], min(flow - use, r[i]));
r[i] -= tmp, r[i ^ 1] += tmp, use += tmp;
if(use == flow) return use;
}
cur[x] = fi[x];
if(!(-- co[d[x]])) d[S] = T;
++ co[++ d[x]];
return use;
}
int main() {
freopen("san.in", "r", stdin);
freopen("san.out", "w", stdout);
scanf("%d %d", &n, &m);
S = 2 * n + 1; T = S + 1;
fo(i, 1, n) {
scanf("%d", &a[i]);
if(a[i] > 0) link(S, i, a[i]), link(i + n, T, a[i]), ans += a[i];
else link(i, i + n, -a[i]);
}
fo(i, 1, m) scanf("%d %d", &x, &y), link(x, y, 1 << 30), link(x + n, y + n, 1 << 30);
co[0] = T;
while(d[S] < T) ans -= dg(S, 1 << 30);
pp("%d", ans);
}