[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\),可以看出,如果不规定反应顺序的话,反应的结果可能有多个。