【费用流】loj_3026「ROIR 2018 Day1」管道监控

题意

给出一棵树,每条连边有一个字母。
给出若干个规范,使用有耗费。
用若干规范去覆盖这颗树使得每条边至少被覆盖一遍,求最小耗费及覆盖方案。

思路

利用网络流处理覆盖问题。
对于树上的一个子串,如果可以用规范覆盖(利用trie判断),那么在头尾之间连接一条边表示覆盖。
对于两个规范之间有重叠,从父节点向子节点连边,即退回再流上去。
对于一个点有多个流量,将它流出去(具体地,最多有儿子个数的路径会流上来,所以流出去的流量最多是儿子个数-1)。
最后从S连向叶子节点,根节点1连向T。
跑最大流,若能跑满,即有解;输出方案即判断每条边是否有流量。

代码

#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long

struct node {
	int a, b, c;
};
const int inf = 2147483647;
int to[1000001][26], w[1000001], k[1000001];
int n, m, t, cnt, scnt, S, T, ans, ansf, tot = 1;
int fa[1000001], son[1000001];
int v[260001], d[260001], pre[260001], head[260001], ver[520001], next[520001], flow[520001], cost[520001], mode[520001];
std::vector<node> anss;
char c[501], s[1000001];

void insert(char *s, int l, int ww, int mm) {
	int p = 0;
	for (int i = l - 1; i >= 0; i--) {
		int ch = s[i] - 'a';
		if (!to[p][ch]) to[p][ch] = ++cnt;
		p = to[p][ch];
 	}
	if (!w[p])
		w[p] = ww, k[p] = mm;
	else if (w[p] > ww) {
			w[p] = ww;
			k[p] = mm;
		}
}

int spfa() {
	std::queue<int> q;
	for (int i = 1; i <= T; i++)
		d[i] = 1e14 + 1;
	memset(v, 0, sizeof(v));
	memset(pre, 0, sizeof(pre));
	d[S] = 0;
	v[S] = 1;
	q.push(S);
	while (q.size()) {
		int u = q.front();
		q.pop();
		v[u] = 0;
		for (int i = head[u]; i; i = next[i]) {
			if (!flow[i]) continue;
			if (d[ver[i]] > d[u] + cost[i]) {
				d[ver[i]] = d[u] + cost[i];
				pre[ver[i]] = i;
				if (!v[ver[i]]) {
					q.push(ver[i]);
					v[ver[i]] = 1;
				}
			}
		}
	}
	return d[T] <= 1e14;//???
}

void addflow() {
    int i = T, mn = 2147483647;
    while (pre[i]) {
        mn = std::min(mn, flow[pre[i]]);
        i = ver[pre[i] ^ 1];
    }
    ans += mn * d[T];
    ansf += mn;
    i = T;
    while (pre[i]) {
        flow[pre[i]] -= mn;
        flow[pre[i] ^ 1] += mn;
        i = ver[pre[i] ^ 1];
    }
}

void add(int u, int v, int f, int c) {
	ver[++tot] = v, next[tot] = head[u], flow[tot] = f, cost[tot] = c, head[u] = tot;
	ver[++tot] = u, next[tot] = head[v], flow[tot] = 0, cost[tot] = -c, head[v] = tot;
}

signed main() {
	scanf("%lld %lld %lld", &n, &m, &t);
	for (int i = 2; i <= n; i++)
		scanf("%lld %c", &fa[i], &c[i]), son[fa[i]]++;
	for (int i = 1, x; i <= m; i++) {
		scanf("%lld ", &x);
		scanf("%s", s);
		insert(s, strlen(s), x, i);
	}
	S = n + 1, T = n + 2;
	for (int i = 2; i <= n; i++) {
	    for (int x = i, p = 0; x != 1; x = fa[x]) {
			int ch = c[x] - 'a';
			if (!to[p][ch])
				break;
			p = to[p][ch];
			if (w[p])
				add(i, fa[x], inf, w[p]), mode[tot - 1] = k[p];
		}
		add(fa[i], i, inf, 0);
		if (son[i] >= 2)
			add(i, T, son[i] - 1, 0);
		if (!son[i])
			add(S, i, 1, 0), scnt++;
	}
	add(1, T, son[1], 0);
	while (spfa())
		addflow();
	if (ansf != scnt)
		return !printf("-1");
	printf("%lld", ans);
	if (t) {
		for (int i = 2; i <= tot; i++)
			if (mode[i] && flow[i ^ 1])
				anss.push_back((node){ver[i], ver[i ^ 1], mode[i]});
		printf("\n%lld\n", anss.size());
		for (int i = 0; i != anss.size(); i++)
			printf("%lld %lld %lld\n", anss[i].a, anss[i].b, anss[i].c);
	}
}

输出完\(-1\)没有退出程序
没有考虑到最短路的最大值

posted @ 2021-08-08 21:26  nymph181  阅读(30)  评论(0)    收藏  举报