CF717G Underfail 题解

题意:

若干区间,区间有权值,选择一个子集,使得权值和尽量大并且每个点不被覆盖超过 \(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;
} 
posted @ 2024-06-08 20:31  rlc202204  阅读(6)  评论(0编辑  收藏  举报