BZOJ 3712: [PA2014]Fiolki 倍增+想法
3712: [PA2014]Fiolki
Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 437 Solved: 115
[Submit][Status][Discuss]
Description
化学家吉丽想要配置一种神奇的药水来拯救世界。
吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号)。初始时,第i个瓶内装着g[i]克的第i种物质。吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到。瓶子的容量可以视作是无限的。
吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直到某一反应物耗尽。生成的沉淀不会和任何物质反应。当有多于一对可以发生反应的物质在一起时,吉丽知道它们的反应顺序。每次倾倒完后,吉丽会等到反应结束后再执行下一步骤。
吉丽想知道配置过程中总共产生多少沉淀。
Input
第一行三个整数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]),按照反应的优先顺序给出。同一个反应不会重复出现。
Output
Sample Input
2 3 4
1 2
3 2
2 3
Sample Output
HINT
Source
想法:
先看出树结构,然后分析:
一个反应最多进行一次,而且反应一定是在其LCA处反应。先不考虑中途其他反应,那么这个反应进行的时间是可以知道的。将这些反应按(反应时间,优先顺序)sort一下。模拟即可。
求反应时间就是求两个点最慢到LCA的时间,可以倍增。O(nlogn+klogn)
还有一种方法,按操作顺序,每操作一次建一个新容器用来装反应完的产物,例:x->y 这样: x->tmp y->tmp 然后y=tmp用来和后面的反应。
这样节点的深度与反应时间反相关,就可以不用倍增求,而用Tarjan求。从而O(n+k)
附上倍增代码
#include<cstdio> #include<cmath> #include<algorithm> typedef long long ll; const int len(200000),lem(500000),INF(0x7fffffff); int n,m,k,g[len+10],a,b,logg; int f[20][len+10],t[len+10],depth[len+10]; struct AB{int a,b,lca,id;}reaction[lem+10]; struct Node{int nd,nx;}bot[len+10];int tot,first[len+10]; ll precipitation,ans; int max(int a,int b){return a>b?a:b;} int min(int a,int b){return a<b?a:b;} bool cmp(AB A,AB B){return (A.lca<B.lca)||(A.lca==B.lca&&A.id<B.id);} void add(int a,int b){bot[++tot]=(Node){b,first[a]};first[a]=tot;} void swap(int &a,int &b){if(a==b)return;a^=b;b^=a;a^=b;} template <class T>void read(T &x) { x=0;int f=0;char c=getchar(); while((c<'0'||c>'9')&&c!='-')c=getchar(); if(c=='-')f=1,c=getchar(); while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} x=f?-x:x; } void DFS(int x) { depth[x]++; for(int v=first[x];v;v=bot[v].nx) depth[bot[v].nd]=depth[x],DFS(bot[v].nd); } int lca(int a,int b) { if(depth[a]<depth[b])swap(a,b); for(int k=depth[a]-depth[b]-1,j=0;k>0;k>>=1,j++) if(k&1)a=f[j][a]; if(f[0][a]==b)return t[a]; if(depth[a]>depth[b]) a=f[0][a]; for(int j=logg;f[0][a]!=f[0][b];j--) if(f[j][a]!=f[j][b])a=f[j][a],b=f[j][b]; return max(t[a],t[b]); } int main() { // freopen("C.in","r",stdin); // freopen("C.out","w",stdout); read(n),read(m),read(k); for(int i=1;i<=n;i++)read(g[i]); for(int i=1;i<=m;i++) { read(a),read(b); f[0][a]=b; t[a]=i; add(b,a); } for(int i=1;i<=n;i++)if(!f[0][i])f[0][i]=n+1,add(n+1,i),t[i]=INF; DFS(n+1); logg=log2(n); for(int j=1;j<=logg;j++) for(int i=1;i<=n;i++) f[j][i]=f[j-1][ f[j-1][i] ]; for(int i=1;i<=k;i++) { read(a),read(b); reaction[i].a=a; reaction[i].b=b; reaction[i].lca=lca(a,b); // if(!reaction[i].lca)fprintf(stderr,"%d\n",i); reaction[i].id=i; } std::sort(reaction+1,reaction+1+k,cmp); for(int i=1;i<=k;i++) if(reaction[i].lca!=INF) { precipitation=min(g[reaction[i].a],g[reaction[i].b]); ans+=precipitation; g[reaction[i].a]-=precipitation; g[reaction[i].b]-=precipitation; }else break; printf("%lld",ans<<1); return 0; }