CodeForces 1525F Goblins And Gnomes

发布时间 2023-07-11 17:09:10作者: zltzlt

洛谷传送门

CF 传送门

套路地,将 DAG 的最小不交路径覆盖转化为点数减去拆点以后最大匹配。感性理解就是一开始每个点都是一条路径,一个匹配意味着两条路径结合了。

由题意知,第 \(i\) 次进攻时最小不交路径覆盖必须 \(> i\),也就是说,设最大匹配为 \(k\),那么 \(n - k > i\),即 \(k \le n - i - 1\)

同时,通过上面的转化,题中删除某个点所有入边或出边的操作也可以转化为,在二分图上删去一个点,左右都可以。

我们发现,只要 \(k > 0\),我们总能找到一个点,使得删掉后 \(k \gets k - 1\)。因为由 Konig 定理得二分图最大匹配 \(=\) 最小点覆盖,只要从最小点覆盖中随意删除一个点即可。

于是我们可以求出一个删点序列,满足依次删除序列中的点,\(k\) 都能依次减 \(1\)

既然我们已经能够满足任意次删点了,那我们就可以对着每次进攻的参数 \(a_i, b_i\) 做一个 dp,求出最大收益。输出方案就直接依次输出上面求出的删点序列即可。

时间复杂度大概是 \(O(n^5)\)

code
// Problem: F. Goblins And Gnomes
// Contest: Codeforces - Educational Codeforces Round 109 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1525/F
// Memory Limit: 512 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 110;
const int maxm = 10000;
const int inf = 0x3f3f3f3f;

int n, m, K, a[maxn], id[maxn][2], ntot, S, T, head[maxn], len, di[maxn];
ll f[maxn][maxn], g[maxn][maxn];

bool vis[maxn];
pii E[maxm];

struct edge {
	int to, next, cap, flow;
} edges[maxm];

inline void add_edge(int u, int v, int c, int f) {
	edges[++len].to = v;
	edges[len].next = head[u];
	edges[len].cap = c;
	edges[len].flow = f;
	head[u] = len;
}

struct Dinic {
	int d[maxn], cur[maxn];
	bool vis[maxn];
	
	inline void add(int u, int v, int c) {
		add_edge(u, v, c, 0);
		add_edge(v, u, 0, 0);
	}
	
	inline bool bfs() {
		for (int i = 1; i <= ntot; ++i) {
			vis[i] = 0;
			d[i] = -1;
		}
		queue<int> q;
		q.push(S);
		d[S] = 0;
		vis[S] = 1;
		while (q.size()) {
			int u = q.front();
			q.pop();
			for (int i = head[u]; i; i = edges[i].next) {
				edge &e = edges[i];
				if (!vis[e.to] && e.cap > e.flow) {
					vis[e.to] = 1;
					d[e.to] = d[u] + 1;
					q.push(e.to);
				}
			}
		}
		return vis[T];
	}
	
	int dfs(int u, int a) {
		if (u == T || !a) {
			return a;
		}
		int flow = 0, f;
		for (int &i = cur[u]; i; i = edges[i].next) {
			edge &e = edges[i];
			if (d[e.to] == d[u] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) {
				e.flow += f;
				edges[i ^ 1].flow -= f;
				flow += f;
				a -= f;
				if (!a) {
					break;
				}
			}
		}
		return flow;
	}
	
	inline int solve() {
		int flow = 0;
		while (bfs()) {
			for (int i = 1; i <= ntot; ++i) {
				cur[i] = head[i];
			}
			flow += dfs(S, inf);
		}
		return flow;
	}
} solver;

inline int work() {
	len = 1;
	mems(head, 0);
	for (int i = 1; i <= n; ++i) {
		if (!vis[id[i][0]]) {
			solver.add(S, id[i][0], 1);
		}
		if (!vis[id[i][1]]) {
			solver.add(id[i][1], T, 1);
		}
	}
	for (int i = 1; i <= m; ++i) {
		int u = E[i].fst, v = E[i].scd;
		if (!vis[id[u][0]] && !vis[id[v][1]]) {
			solver.add(id[u][0], id[v][1], 1);
		}
	}
	return solver.solve();
}

void solve() {
	scanf("%d%d%d", &n, &m, &K);
	for (int i = 1; i <= m; ++i) {
		scanf("%d%d", &E[i].fst, &E[i].scd);
	}
	for (int i = 1; i <= n; ++i) {
		id[i][0] = ++ntot;
		di[ntot] = i;
		id[i][1] = ++ntot;
		di[ntot] = -i;
	}
	S = ++ntot;
	T = ++ntot;
	int flow = work();
	for (int i = 1; i <= flow; ++i) {
		for (int j = 1; j <= n * 2; ++j) {
			if (!vis[j]) {
				vis[j] = 1;
				if (work() == flow - i) {
					a[i] = j;
					break;
				}
				vis[j] = 0;
			}
		}
	}
	mems(f, -0x3f);
	f[0][0] = 0;
	for (int i = 1; i <= K; ++i) {
		ll x, y;
		scanf("%lld%lld", &x, &y);
		for (int j = 0; j <= flow; ++j) {
			if (n - (flow - j) <= i) {
				continue;
			}
			for (int k = 0; k <= j; ++k) {
				if (f[i][j] < f[i - 1][k] + max(0LL, x - y * (j - k))) {
					f[i][j] = f[i - 1][k] + max(0LL, x - y * (j - k));
					g[i][j] = k;
				}
			}
		}
	}
	vector<int> ans;
	ll p = 0;
	for (int i = 1; i <= flow; ++i) {
		if (f[K][i] > f[K][p]) {
			p = i;
		}
	}
	for (int i = K, j = p; i; j = g[i--][j]) {
		ans.pb(0);
		for (int k = j; k > g[i][j]; --k) {
			ans.pb(di[a[k]]);
		}
	}
	reverse(ans.begin(), ans.end());
	printf("%d\n", (int)ans.size());
	for (int x : ans) {
		printf("%d ", x);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}