变量取值
题目链接:YBT2023寒假Day2 A
题目大意
有 n 个变量你可以选择取 W 还是 -W,有一些限制形如某个变量要小于或者小于等于或者等于某个变量。
然后还有一些式子,选了三个变量 x,y,z,再让 |wx-wy|,|wy-wz|,|wz-wx|,(wx-wy),(wy-wz),(wz-wx) 六个分别乘上给出的常数加起来。
然后要你最小化变量和以及每个式子的和的和。
思路
考虑两种(有绝对值和没有绝对值的)分开来看。
首先是没有绝对值的,那明显你可以把拆开,变成每个变量乘上一个常数(然后因为要加上变量自己的值,所以我们除了式子里面的每个变量的常数要加一)
然后是有绝对值的,会发现这个值(不看常数),就只能是
0
0
0 或者
2
W
2W
2W。
然后会发现
2
W
2W
2W 的情况就是两个变量的取值不同。
然后继续考虑限制条件。
x
⩽
y
x\leqslant y
x⩽y:那就是不能是
x
=
W
,
y
=
−
W
x=W,y=-W
x=W,y=−W
x
=
y
x=y
x=y:那就是不能是
x
=
W
,
y
=
−
W
x=W,y=-W
x=W,y=−W,也不能是
x
=
−
W
,
y
=
W
∣
x=-W,y=W|
x=−W,y=W∣
x
<
y
x<y
x<y:那就是只能
x
=
−
W
,
y
=
W
x=-W,y=W
x=−W,y=W
那这个限制以及出现一些情况就要给费用,而且只有两种形态,那你不就是一个最小割模型吗。
考虑割源点的边就是选
W
W
W,割汇点的就是
−
W
-W
−W。
前面的很简单,两个变量取值不同就是之间弄双向边。
然后看限制条件,比如不能是
x
=
W
,
y
=
−
W
x=W,y=-W
x=W,y=−W 这个,思考一下会发现你就
y
→
x
y\rightarrow x
y→x 连边即可。
然后
x
=
y
x=y
x=y 那个就是连双向边。
x
<
y
x<y
x<y 那个你转化为
x
=
W
x=W
x=W 不行,
y
=
−
W
y=-W
y=−W 也不行,那就是选这两个要用无限大的流量。
那建出来跑最大流(最小割)即可,然后题目保证有解所以就可以了。
代码
#include<queue>
#include<cstdio>
#include<iostream>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 550;
const ll inf = 1e12;
struct node {
ll x; int to, nxt, op;
}e[N * N * 21];
int n, w, p, q, le[N * 3], KK, S, T, tot;
ll A[N], B[N][N];
ll Abs(ll x) {return x < 0 ? -x : x;}
void clean() {
KK = 0; for (int i = 1; i <= tot; i++) le[i] = 0;
for (int i = 1; i <= n; i++) {
A[i] = 0;
for (int j = 1; j <= n; j++) B[i][j] = 0;
}
}
void add(int x, int y, ll z) {
e[++KK] = (node){z, y, le[x], KK + 1}; le[x] = KK;
e[++KK] = (node){0, x, le[y], KK - 1}; le[y] = KK;
}
queue <int> qq; int lee[N * 3], deg[N * 3];
bool bfs() {
for (int i = 1; i <= tot; i++) lee[i] = le[i], deg[i] = 0;
while (!qq.empty()) qq.pop();
qq.push(S); deg[S] = 1;
while (!qq.empty()) {
int now = qq.front(); qq.pop();
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].x && !deg[e[i].to]) {
deg[e[i].to] = deg[now] + 1;
if (e[i].to == T) return 1;
qq.push(e[i].to);
}
}
return 0;
}
ll dfs(int now, ll sum) {
if (now == T) return sum;
ll go = 0;
for (int &i = lee[now]; i; i = e[i].nxt)
if (e[i].x && deg[now] + 1 == deg[e[i].to]) {
ll this_go = dfs(e[i].to, min(sum - go, e[i].x));
if (this_go) {
e[i].x -= this_go;
e[e[i].op].x += this_go;
go += this_go; if (go == sum) return go;
}
}
deg[now] = -1; return go;
}
ll dinic() {
ll re = 0;
while (bfs())
re += dfs(S, INF);
return re;
}
void slove() {
scanf("%d %d %d %d", &n, &w, &p, &q);
S = n + 1; T = n + 2; tot = T;
for (int i = 1; i <= n; i++) A[i] = 1;
for (int i = 1; i <= p; i++) {
ll x, y, z, a, b, c, d, e, f;
scanf("%lld %lld %lld %lld %lld %lld %lld %lld %lld", &x, &y, &z, &a, &b, &c, &d, &e, &f);
B[x][y] += a; B[y][z] += b; B[z][x] += c;
A[x] += d - f; A[y] += e - d; A[z] += f - e;
}
ll dians = 0;
for (int i = 1; i <= n; i++) {
dians -= Abs(A[i]);
if (A[i] > 0) add(S, i, 2 * A[i]);
if (A[i] < 0) add(i, T, -2 * A[i]);
for (int j = i + 1; j <= n; j++)
if (B[i][j] + B[j][i]) add(i, j, 2 * (B[i][j] + B[j][i])), add(j, i, 2 * (B[i][j] + B[j][i]));
}
for (int i = 1; i <= q; i++) {
ll x, y, r;
scanf("%lld %lld %lld", &x, &y, &r);
if (r == 0) add(y, x, inf);
if (r == 1) add(x, y, inf), add(y, x, inf);
if (r == 2) add(S, x, inf), add(y, T, inf);
}
printf("%lld\n", (dinic() + dians) * w);
clean();
}
int main() {
freopen("variable.in", "r", stdin);
freopen("variable.out", "w", stdout);
int T; scanf("%d", &T);
while (T--) slove();
return 0;
}