[PA2014]Fiolki

Description

BZOJ3712
有n个瓶子装着n种物质,有k个反应,每个反应表示1g\(a\)和1g\(b\)反应生成2g沉淀,反应优先级为给定顺序,有m个操作形如将瓶子a中的物质倒入瓶子b中(保证a,b非空)。求最后生成的沉淀。

Solution

容易看出操作构成了一棵树,那么,把操作看成连边的话,某个反应发生的时间就是参与该反应的两种物质联通的时间!重构树!
按着这个思路,用操作建出重构树,把反应按着重构树上的LCA深度和反应优先级排序就行了,不要忘记反应优先级也是有用的。

Code

#include <cstdio>
#include <algorithm>
#include <vector>

const int N = 200010 * 2;
const int M = 500000 + 10;
const int INF = 0x3f3f3f3f;

std::vector<int> g[N];
int fa[N], tot, n, m, k, w[N];
int sz[N], son[N], dep[N], top[N], pa[N];
struct Reaction {
	int a, b, tim, id;
	bool operator<(const Reaction& x) const {
		return tim == x.tim ? id < x.id : tim > x.tim;
	}
} rct[M];

int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

int merge(int x, int y) {
	int fx = find(x), fy = find(y);
	if (fx == fy) return -1;
	tot++;
	g[tot].push_back(fx);
	g[tot].push_back(fy);
	fa[fx] = fa[fy] = fa[tot] = tot;
	return 0;
}

void dfs(int x, int f) {
	dep[x] = dep[f]+1;
	pa[x] = f;
	sz[x] = 1;
	for (unsigned int i = 0; i < g[x].size(); ++i) {
		dfs(g[x][i], x);
		sz[x] += sz[g[x][i]];
		if (sz[g[x][i]] > sz[son[x]]) son[x] = g[x][i];
	}
}

void rebuild(int x, int f) {
	top[x] = f;
	if (son[x]) rebuild(son[x], f);
	for (unsigned int i = 0; i < g[x].size(); ++i) if (g[x][i] != son[x]) {
		rebuild(g[x][i], g[x][i]);
	}
}

int lca(int x, int y) {
	if (find(x) != find(y)) return -INF;
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) y = pa[top[y]];
		else x = pa[top[x]];
	}
	return dep[x] < dep[y] ? dep[x] : dep[y];
}

int main() {
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &w[i]);
		fa[i] = i;
	}
	tot = n;
	for (int i = 1, x, y; i <= m; ++i) {
		scanf("%d%d", &x, &y);
		merge(x, y);
	}
	for (int i = tot; i; --i) if (!sz[i]) {
		dfs(i, 0);
		rebuild(i, i);
	}
	for (int i = 1; i <= k; ++i) {
		scanf("%d%d", &rct[i].a, &rct[i].b);
		rct[i].tim = lca(rct[i].a, rct[i].b);
		rct[i].id = i;
	}
	std::sort(rct+1, rct+k+1);
	long long ans = 0;
	for (int i = 1; i <= k; ++i) {
		if (rct[i].tim == -INF) break;
		long long s = std::min(w[rct[i].a], w[rct[i].b]);
		ans += s * 2LL;
		w[rct[i].a] -= s;
		w[rct[i].b] -= s;
	}
	printf("%lld\n", ans);
	return 0;
}

Note

关于反应优先级的问题,这有一个栗子:四种物质,分别为\(8g, 4g, 2g, 2g\),反应为\(1+2,2+3,3+4\),操作是\(1-4, 2-3, 3-4\),可以看出,如果不规定反应顺序的话,反应的结果可能有多个。

posted @ 2018-10-05 18:27  wyxwyx  阅读(99)  评论(0编辑  收藏  举报