【YBT2023寒假Day2 A】变量取值(网络流)

变量取值

题目链接:YBT2023寒假Day2 A

题目大意

有 n 个变量你可以选择取 W 还是 -W,有一些限制形如某个变量要小于或者小于等于或者等于某个变量。
然后还有一些式子,选了三个变量 x,y,z,再让 |wx-wy|,|wy-wz|,|wz-wx|,(wx-wy),(wy-wz),(wz-wx) 六个分别乘上给出的常数加起来。
然后要你最小化变量和以及每个式子的和的和。

思路

考虑两种(有绝对值和没有绝对值的)分开来看。

首先是没有绝对值的,那明显你可以把拆开,变成每个变量乘上一个常数(然后因为要加上变量自己的值,所以我们除了式子里面的每个变量的常数要加一)
然后是有绝对值的,会发现这个值(不看常数),就只能是 \(0\) 或者 \(2W\)
然后会发现 \(2W\) 的情况就是两个变量的取值不同。

然后继续考虑限制条件。
\(x\leqslant y\):那就是不能是 \(x=W,y=-W\)
\(x=y\):那就是不能是 \(x=W,y=-W\),也不能是 \(x=-W,y=W|\)
\(x<y\):那就是只能 \(x=-W,y=W\)

那这个限制以及出现一些情况就要给费用,而且只有两种形态,那你不就是一个最小割模型吗。
考虑割源点的边就是选 \(W\),割汇点的就是 \(-W\)
前面的很简单,两个变量取值不同就是之间弄双向边。

然后看限制条件,比如不能是 \(x=W,y=-W\) 这个,思考一下会发现你就 \(y\rightarrow x\) 连边即可。
然后 \(x=y\) 那个就是连双向边。
\(x<y\) 那个你转化为 \(x=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;
}
posted @ 2023-01-29 22:32  あおいSakura  阅读(27)  评论(0编辑  收藏  举报