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
鸣谢Jcvb
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