CF717G Underfail 題解

rlc202204發表於2024-06-08

題意:

若干區間,區間有權值,選擇一個子集,使得權值和儘量大並且每個點不被覆蓋超過 \(x\) 次。

\(n \le 500\)

思路:

很神奇的一道題。

我們考慮費用流,如果單純的一邊是區間一邊是點的話其實並不好做,所以這道題我們直接建一排 \(n+2\) 個點,一個區間 \(l, r\) 就從 \(l\)\(r+1\) 連流量 \(1\) 費用 \(p\) 的邊,然後 \(i \to i + 1\) 連流量 \(x\) 費用 \(0\) 的邊。

顯然最大流肯定是 \(x\),則 \(i \to i + 1\) 如果流了 \(f\) 個過去,說明有 \(x-f\) 個是從區間的邊過去的,這就表示了區間覆蓋,且顯然 \(f \le x\),則方案合法。

建模過於神奇了。

點選檢視程式碼
#include <iostream>
#include <vector>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 505;
const int inf = 0x3f3f3f3f;

struct Edge {
	int to, val, cst, rev;
	Edge (int _to = 0, int _val = 0, int _cst = 0, int _rev = 0) :
		to(_to), val(_val), cst(_cst), rev(_rev) {}
};
vector<Edge> e[N];
void add(int u, int v, int w, int c) {
	e[u].push_back(Edge(v, w, c, (int)e[v].size()));
	e[v].push_back(Edge(u, 0, -c, (int)e[u].size() - 1));
}

int d[N] = {0};
bool inq[N] = {false};
bool bfs(int s, int t) {
	memset(d, inf, sizeof d);
	queue<int> q;
	q.push(s), d[s] = 0;
	while (!q.empty()) {
		int h = q.front();
		q.pop();
		inq[h] = false;
		for (auto i: e[h])
			if (i.val > 0 && d[i.to] > d[h] + i.cst) {
				d[i.to] = d[h] + i.cst;
				if (!inq[i.to])
					inq[i.to] = true, q.push(i.to);
			}
	}
	return d[t] != inf;
}

int cur[N] = {0};
bool vis[N] = {false};
int dfs(int x, int t, int f) {
	if (x == t)
		return f;
	int fl = 0;
	vis[x] = true;
	for (int i = cur[x]; i < (int)e[x].size(); i = ++cur[x])
		if (e[x][i].val > 0 && !vis[e[x][i].to] && d[e[x][i].to] == d[x] + e[x][i].cst) {
			fl = dfs(e[x][i].to, t, min(f, e[x][i].val));
			if (fl > 0) {
				e[x][i].val -= fl;
				e[e[x][i].to][e[x][i].rev].val += fl;
				break;
			}
		}
	if (fl == 0)
		d[x] = -1;
	vis[x] = false;
	return fl;
}

int Dinic(int s, int t) {
	int ans = 0;
	while (bfs(s, t)) {
		memset(cur, 0, sizeof cur);
		int ad = 0;
		while ((ad = dfs(s, t, inf)) > 0)
			ans += ad * d[t];
	}
	return ans;
}

int n, m, k;
string s, t[N];
int p[N] = {0};

int main() {
	cin >> n >> s >> m;
	for (int i = 1; i <= m; i++) {
		cin >> t[i] >> p[i];
		for (int j = 0; j + (int)t[i].size() - 1 < (int)s.size(); j++)
			if (s.substr(j, (int)t[i].size()) == t[i])
				add(j + 1, j + (int)t[i].size() + 1, 1, -p[i]);
	}
	cin >> k;
	for (int i = 1; i <= n + 1; i++)
		add(i, i + 1, k, 0);
	cout << -Dinic(1, n + 2) << endl;
	return 0;
}