【BZOJ-3712】Fiolki LCA + 倍增 (idea题)
3712: [PA2014]Fiolki
Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 303 Solved: 67
[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
Solution
idea比较好的一道题,不是特别容易想到
很容易发现是树形结构,那么考虑把树建出来
这里自己想到了,但是忽略了一点,自己的想法是想如果x流进y,那么就建树边x-->y,但实际上是不可以的,正确的做法是新建一个节点X',x-->X',y-->X',然后把y的标号换为X',这样就可以了,很容易理解;(但要注意的是,这里建出的实际上是森林,可以DFS按时间戳划分)
那么题目就转化为树上的了,那么一个反应的询问,实际上就是找LCA,那么用倍增去找LCA即可
注意反应的顺序,那么可以按LCA的深度为第一关键字,id为第二关键字排序,统计答案就可以了
idea:
模型的转化,就如同SDOI2016省队集训R1Day4T3,转化到树上,就能简化问题,利用其性质得出结果
注意Code时的细节,避免手误
像如此转化成树的问题,不要总想直接转化,可以考虑加额外的点,这种思想 BZOJ3551Peaks加强版 的Kruskal重构树中有很好的体现
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-')f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define maxn 500010 long long ans; struct EdgeNode{int next,to;}edge[maxn<<1]; int head[maxn],cnt=1; int n,m,k,t,g[maxn],fa[maxn],father[maxn][21],deep[maxn],dfn[maxn]; struct Node { int x,y,dp,id; Node () {} Node (int a,int b,int c,int d) {x=a;y=b;dp=c;id=d;} bool operator < (const Node & A) const {return dp==A.dp?id<A.id:dp>A.dp;} }tmp[maxn];int tot; void add(int u,int v) {cnt++;edge[cnt].next=head[u];head[u]=cnt;edge[cnt].to=v;} void insert(int u,int v) {add(u,v);add(v,u);} void DFS(int now,int tim) { dfn[now]=tim; for (int i=1; i<=20; i++) if ((1<<i)<=deep[now]) father[now][i]=father[father[now][i-1]][i-1]; else break; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=father[now][0]) { deep[edge[i].to]=deep[now]+1; father[edge[i].to][0]=now; DFS(edge[i].to,tim); } } int LCA(int x,int y) { if (deep[x]<deep[y]) swap(x,y); int dd=deep[x]-deep[y]; for (int i=0; (1<<i)<=dd; i++) if (dd&(1<<i)) x=father[x][i]; for (int i=20; i>=0; i--) if (father[x][i]!=father[y][i]) x=father[x][i],y=father[y][i]; if (x==y) return x; return father[x][0]; } int main() { n=read(); m=read(); k=read(); for (int i=1; i<=n; i++) g[i]=read(); for (int i=1; i<=n; i++) fa[i]=i; for (int x,y,i=1; i<=m; i++) x=read(),y=read(),insert(n+i,fa[x]),insert(n+i,fa[y]),fa[y]=n+i; for (int i=n+m; i; i--) if (!father[i][0]) DFS(i,++t); // printf("%d\n",t); for (int x,y,i=1; i<=k; i++) { x=read(),y=read(); if (dfn[x]==dfn[y]) tmp[++tot]=Node(x,y,deep[LCA(x,y)],i); } sort(tmp+1,tmp+tot+1); for (int x,y,cd,i=1; i<=tot; i++) x=tmp[i].x,y=tmp[i].y,cd=min(g[x],g[y]),g[x]-=cd,g[y]-=cd,ans+=(long long)cd; // printf("%d\n",tot); printf("%lld",(long long)ans<<1); return 0; }
我是不会告诉你,WA成这个狗样是因为自己快速读入写错了,智障+10