CF1430G Yet Another DAG Problem

CF1430G

最小割:

對於 \(w_i\times(a_u-a_v)\), 拆成 \(w_ia_u\)\(-w_ia_v\), 這樣就把權重拆到了點上。

首先顯然地, 每個點的權值都在 [0,n-1] 之間, 於是可以跑最小割。

要保證網絡圖裏的邊權爲整數, 給所有邊加上同一個較大值即可。

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;

const int N = 23, M = 1e5+23;
const LL bignum = 4e11, inf = 1ll << 60;

int n, w[N], m;
int id[N][N], tot, S, T;

int ecnt = 1, hd[M], cur[M], nt[M], vr[M];
LL cap[M];
void ad(int x, int y, LL w) {
	nt[++ecnt] = hd[x], hd[x] = ecnt;
	vr[ecnt] = y, cap[ecnt] = w;
}
void ad_e(int x, int y, LL w) {
	ad(x, y, w), ad(y, x, 0);
}

int q[M], h, t, d[M];
bool bfs() {
	for (int i = 1; i <= tot; ++i) d[i] = -1;
	d[q[h = t = 1] = S] = 0;
	while (h <= t) {
		int x = q[h ++];
		for (int i = hd[x],y; i; i = nt[i])
			if (cap[i] && -1 == d[y = vr[i]])
				d[q[++t] = y] = d[x] + 1;
	}
	return -1 != d[T];
}

LL dfs(int x, LL flw) {
	if (x == T) return flw;
	LL rst = flw;
	for (int &i=cur[x],y; i; i = nt[i])
		if (cap[i] && d[y = vr[i]] == d[x] + 1) {
			LL tmp = dfs(y, min(cap[i], rst));
			if(!tmp) d[y] = -1;
			else cap[i] -= tmp, cap[i^1] += tmp, rst -= tmp;
			if(!rst) break;
		}
	return flw - rst;
}

bool vis[M];
void dfs2 (int x) {
	vis[x] = true;
	for (int i = hd[x]; i; i = nt[i])
		if(cap[i] && !vis[vr[i]])
			dfs2(vr[i]);
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++ i) for (int j = 0; j <= n; ++ j) id[i][j] = ++ tot;
	S = ++tot, T = ++tot;
	for (int i = 0; i < m; ++ i) {
		int x, y, z; scanf("%d%d%d", &x, &y, &z);
		w[x] += z, w[y] -= z;
		for (int j = 0; j <= n-1; ++ j) ad_e(id[y][j], id[x][j+1], inf);
	}
	for (int i = 1; i <= n; ++ i) {
		ad_e(S, id[i][0], inf), ad_e(id[i][n], T, inf);
		for (int j = 0; j < n; ++ j) ad_e(id[i][j], id[i][j+1], (LL)w[i] * j + bignum);
	}
	while (bfs()) {
		for (int i = 1; i <= tot; ++ i) cur[i] = hd[i];
		int now = 0;
		while ((now = dfs(S, inf)));
	}
	dfs2(S);
	for (int i = 1; i <= n; ++ i) {
		int ans = 0;
		for (int j = 0; j < n; ++ j) if(vis[id[i][j]]) ans = j;
		cout << ans << ' ';
	}
	return 0;
}

狀壓:

可以將 DAG 分層, 相鄰兩層的點權差爲 1, 發現顯然是可以做到的, 最後從 dp 狀態逆推點權即可。

#include <bits/stdc++.h>
typedef long long LL;

using namespace std;

const int N = 18;
const LL inf = 1e18;

int n, m, e[N][N], in[N], lg[1 << N];
int Sin[1 << N];
LL Soutval[1 << N];
LL dp[1 << N];
int pre[1 << N];

#define lowb(x) (x&(-x))

int ans[N];
void giao(int x, int v) {
	if(!x) return;
	giao(pre[x], v + 1);
	x = pre[x] ^ x;
	while (x) {
		ans[lg[lowb(x)]] = v;
		x &= x-1;
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	int all = (1 << n) - 1;
	for (int i = 0; i < m; ++ i) {
		int x, y, z; scanf("%d%d%d", &x, &y, &z);
		--x, --y;
		e[x][y] = z;
		in[y] |= 1 << x;
	}
	for (int i = 0; i < N; ++ i) lg[1 << i] = i;
	for (int S = 1; S < (1 << n); ++ S) {
		int x = lowb(S), lgx = lg[x];
		Sin[S] = Sin[S ^ x] | in[lgx];
		LL up = 0ll, down = 0ll;
		for (int j = 0; j < n; ++j)
			if((S >> j) & 1)
				down += e[j][lgx];
			else
				up += e[lgx][j];
		Soutval[S] = Soutval[S ^ x] + up - down;
		dp[S] = inf;
	}
	for (int S = 0; S < (1 << n); ++ S) {
		int T = all ^ S;
		for (int nS = T; nS; nS = T & (nS - 1))
			if((S & Sin[nS]) == Sin[nS]) {
				if(dp[S | nS] > dp[S] + Soutval[S]) {
					dp[S | nS] = dp[S] + Soutval[S];
					pre[S | nS] = S;
				}
			}
	}
	giao(all, 0);
	for (int i = 0; i < n; ++i) cout << ans[i] << ' ';
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章