Fiolki
Posted moyiii
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Fiolki相关的知识,希望对你有一定的参考价值。
时间限制: 3 Sec 内存限制: 128 MB
题目描述
化学家吉丽想要配置一种神奇的药水来拯救世界。
吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号)。初始时,第i个瓶内装着g[i]克的第i种物质。吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到。瓶子的容量可以视作是无限的。
吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直到某一反应物耗尽。生成的沉淀不会和任何物质反应。当有多于一对可以发生反应的物质在一起时,吉丽知道它们的反应顺序。每次倾倒完后,吉丽会等到反应结束后再执行下一步骤。
吉丽想知道配置过程中总共产生多少沉淀。
输入
第一行三个整数n,m,k(0<=m<n<=200000,0<=k<=500000),分别表示药瓶的个数(即物质的种数),操作步数,可以发生的反应数量。
第二行有n个整数g[1],g[2],…,g[n](1<=g[i]<=10^9),表示初始时每个瓶内物质的质量。
接下来m行,每行两个整数a[i],b[i](1<=a[i],b[i]<=n,a[i]≠b[i]),表示第i个步骤。保证a[i]在以后的步骤中不再出现。
接下来k行,每行是一对可以发生反应的物质c[i],d[i](1<=c[i],d[i]<=n,c[i]≠d[i]),按照反应的优先顺序给出。同一个反应不会重复出现。
输出
配置过程中总共产生多少沉淀。
样例输入
3 2 1
2 3 4
1 2
3 2
2 3
样例输出
6
题解
题目并不复杂,而且有很多可以利用的性质,比如说每个反应只会进行一次,所有物质在遇到反应物之前并没有什么用之类的,但是还是只想到了n^2的并查集+链表做法,水了30。通过这道题学会了一种很神奇的东西叫重构树,即每倾倒一次就新建一个节点作为这两个节点的父亲,然后以此类推,可以想见每一个反应一定是在LCA上发生的,然后我们就可以欢快地按优先级处理每个点发生的反应,只要倒着加边反应就会按优先级从大到小进行。重构树作为一种新思路让这道题一下子简明了起来,不过我实现得有点繁冗,因为用的是离线LCA,再加上把每个反应倒序加到LCA上一共用了三个邻接表两个并查集。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int sj1=200005; const int sj2=500005; const int sj3=sj1+sj2; int n,m,k,f[sj3],g[sj1],yz[sj2],yy[sj2],h[sj1],zx[sj3]; int tp,d[sj3],e1,e2,lca[sj2],fa[sj2],size,w[sj3]; long long ans; bool r[sj3]; int read() { int jg=0,jk=getchar()-‘0‘; if(jk<=9&&jk>0) jg=jk; jk=getchar()-‘0‘; while(jk<=9&&jk>=0) jg=jg*10+jk,jk=getchar()-‘0‘; return jg; } int find(int x) { if(f[x]==-1) return x; f[x]=find(f[x]); return f[x]; } void hb(int x,int y) { x=find(x),y=find(y); if(x!=y) f[x]=y; } int fi(int x) { if(w[x]==-1) return x; w[x]=fi(w[x]); return w[x]; } void hbb(int x,int y) { x=fi(x),y=fi(y); if(x!=y) w[x]=y; } struct tree { int v,ne,num; }t[sj2<<1]; struct B { int v,ne; }b[sj2<<2]; void ad1(int x,int y,int z) { t[e1].v=y,t[e1].ne=h[x]; t[e1].num=z,h[x]=e1++; } void ad2(int x,int y) { b[e2].v=y,b[e2].ne=d[x]; d[x]=e2++; } void init() { memset(d,-1,sizeof(d)); memset(h,-1,sizeof(h)); memset(f,-1,sizeof(f)); memset(w,-1,sizeof(w)); n=read(),m=read(),k=read(); size=n; for(int i=1;i<=n;i++) g[i]=read(); for(int i=1;i<=m;i++) { tp=read(),e1=read(); size++; ad2(find(tp),size),ad2(find(e1),size); ad2(size,find(tp)),ad2(size,find(e1)); hb(tp,size),hb(e1,size); } for(int i=1;i<=k;i++) yz[i]=read(),yy[i]=read(); e1=0; for(int i=k;i>=1;i--) ad1(yz[i],yy[i],i),ad1(yy[i],yz[i],i); } void tarjan(int x) { r[x]=1,zx[x]=x; for(int i=d[x];i!=-1;i=b[i].ne) { if(!r[b[i].v]) { tarjan(b[i].v); hb(x,b[i].v); zx[find(b[i].v)]=x; } hbb(x,b[i].v); } if(x<=n) for(int i=h[x];i!=-1;i=t[i].ne) if(r[t[i].v]&&fi(x)==fi(t[i].v)) fa[t[i].num]=zx[find(t[i].v)]; } int main() { init(); memset(f,-1,sizeof(f)); for(int i=n+m;i>=1;i--) if(!r[i]) tarjan(i); memset(b,0,sizeof(b)); memset(d,-1,sizeof(d)); for(int i=k;i>=1;i--) if(fa[i]) ad2(fa[i],i); for(int i=n+1;i<=n+m;i++) for(int j=d[i];j!=-1;j=b[j].ne) { tp=b[j].v; if(g[yy[tp]]==0||g[yz[tp]]==0) continue; if(g[yy[tp]]<=g[yz[tp]]) { ans+=g[yy[tp]]; g[yz[tp]]-=g[yy[tp]],g[yy[tp]]=0; } else { ans+=g[yz[tp]]; g[yy[tp]]-=g[yz[tp]],g[yz[tp]]=0; } } printf("%lld",ans<<1); return 0; }
以上是关于Fiolki的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ-3712Fiolki LCA + 倍增 (idea题)