【BZOJ】3712: [PA2014]Fiolki

http://www.lydsy.com/JudgeOnline/problem.php?id=3712

题意:n个瓶子,第i个瓶子里又g[i]克物质。m次操作,第i次操作把第a[i]个瓶子的东西全部倒到第b[i]个瓶子里(保证之后不出现a[i])。k种反应,其中c[i]和d[i]反应,而且如果一个瓶子里有多种反应则优先反应靠前的。每次反应对答案贡献为min(g[i], g[i])*2(m<n<=200000, k<=500000)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200005, M=200005, nQ=500005;
ll ans;
int n, m, K, ihead[N+M], cnt, pos[N], g[N], dep[N+M], f[N+M][20], tot;
struct E { int next, to; } e[(N+M)<<1];
struct Q { int x, y, ff, dep, id; } q[nQ];
bool cmp(const Q &a, const Q &b) {
	return a.dep==b.dep?a.id<b.id:a.dep>b.dep;
}
void add(int x, int y) {
	e[++cnt]=(E){ihead[x], y}; ihead[x]=cnt;
	e[++cnt]=(E){ihead[y], x}; ihead[y]=cnt;
}
void dfs(int x) {
	for(int i=1; i<=19; ++i) f[x][i]=f[f[x][i-1]][i-1];
	for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=f[x][0]) { f[e[i].to][0]=x; dep[e[i].to]=dep[x]+1; dfs(e[i].to); }
}
int lca(int x, int y) {
	if(dep[x]<dep[y]) swap(x, y);
	int d=dep[x]-dep[y];
	for(int i=19; i>=0; --i) if((d>>i)&1) x=f[x][i];
	if(x==y) return x;
	for(int i=19; i>=0; --i) if(f[x][i]!=f[y][i]) x=f[x][i], y=f[y][i];
	return f[x][0];
}
int main() {
	scanf("%d%d%d", &n, &m, &K);
	for(int i=1; i<=n; ++i) pos[i]=i;
	for(int i=1; i<=n; ++i) scanf("%d", &g[i]);
	for(int i=1; i<=m; ++i) { int a, b; scanf("%d%d", &a, &b); int ff=n+i; add(pos[a], ff); add(pos[b], ff); pos[b]=ff; }	
	for(int i=n+m; i>=1; --i) if(!f[i][0]) dfs(i);
	for(int i=1; i<=K; ++i) { int a, b, ff; scanf("%d%d", &a, &b); ff=lca(a, b); if(!ff) continue; q[tot++]=(Q){a, b, ff, dep[ff], tot}; }
	sort(q, q+tot, cmp);
	for(int i=0; i<tot; ++i) {
		int x=q[i].x, y=q[i].y;
		int dec=min(g[x], g[y]);
		g[x]-=dec; g[y]-=dec; ans+=dec;
	}
	printf("%lld\n", ans<<1);
	return 0;
}

  

居然是图论题啊orzzzzzzzzzzzz

好题啊orzzzzzzzzz

首先我们每次新建一个节点表示合并的两个瓶子(而题目中的条件表明了这是一个森林!),然后发现询问的话其实就是两个瓶子的lca!

于是按lca的深度及其时间权值排序一下即可。

posted @ 2015-05-16 18:37  iwtwiioi  阅读(568)  评论(0编辑  收藏  举报