P5578 [PA2014] Fiolki 题解

PineappleSummer

2023-08-24 11:22:44

Solution

[题目传送门](https://www.luogu.com.cn/problem/P5578) LCA 好题,细节挺多的,来写个题解吧。 首先分析样例,发现是这么一个图。 ![](https://cdn.luogu.com.cn/upload/image_hosting/zi6q8ime.png) 嗯,这不是仅仅图,这是树。题目上说: >第 $i$ 个步骤是将第 $a_i$ 个瓶子内的所有液体倒入第 $b_i$ 个瓶子,此后第 $a_i$ 个瓶子不会再被用到。 这不就是说一个儿子只能有一个父亲,而一个父亲可以有多个儿子吗?这不就是树? 建树。**注意:应该把两个反应物向生成物连一条边,把 $b$ 自己拆成反应物和生成物,这样就可能会形成森林(不一定联通)。** 此处感谢@[Starlight_Glimmer](https://www.luogu.com.cn/user/41165)题解的指导。 建树代码: ```cpp for(int i=1;i<=m;i++) { int u,v; cin>>u>>v; G[n+i].push_back(pos[u]); G[n+i].push_back(pos[v]); pos[v]=n+i;//pos[v]表示点v的生成物所在的点编号,初始化 pos[v]=v } ``` 我们进一步分析,发现对于可以进行反应的点对 $(u,v)$,如果 $(u,v)$ 有公共祖先(即有 LCA),他们就**可能**进行反应,反之则**不可能**进行反应。 预处理和倍增求 LCA 都是板子,这里就不放了,可以在最后的代码里查看。 ```cpp for(int i=n+m;i>=1;i--) if(!d[i]) bfs(i);//图不一定联通 for(int i=1;i<=k;i++) { int u,v; cin>>u>>v; int LCA=lca(u,v);//求LCA if(!LCA) continue;//没有公共祖先 e[++tot]=Edge{u,v,LCA,d[LCA],i};//如果有,存入e结构体数组 } ``` $e$ 是一个结构体数组,其存储着每一对会发生反应的点对 $(u,v)$,其定义如下: ```cpp struct Edge { int u,v,l,dep,id;//l为u,v的lca,dep为l的深度,id为反应的优先级 }e[K]; ``` $e$ 数组是无序的,而反应是有序的,考虑对 $e$ 数组进行排序。先按照 $l$ 的深度从大到小排序,因为 $l$ 的深度越大,越早发生反应;$l$ 的深度相同的按照反应的优先级从大到小排序。 ```cpp bool cmp(Edge a,Edge b) { return a.dep>b.dep||(a.dep==b.dep&&a.id<b.id); } ``` 遍历每一个可以进行反应的点对 $(u,v)$,沉淀即为 $\min(g_u,g_v)\times 2$,$\min(g_u,g_v)$ 记为 $s$,用 $g_u$ 和 $g_v$ 分别减去 $s$,再拿 $ans$ 加上新增加的沉淀即可。 ```cpp for(int i=1;i<=tot;i++) { int s=min(g[e[i].u],g[e[i].v]); g[e[i].u]=max(0ll,g[e[i].u]-s),g[e[i].v]=max(0ll,g[e[i].v]-s); ans+=(s<<1); } ``` 最后输出 $ans$。 完整代码: ```cpp #include<bits/stdc++.h> #define ll long long using namespace std; const int N=4e5+10,K=5e5+10,logg=21; int n,m,k,d[N],t,f[N][logg],pos[N],tot; vector<int>G[N]; ll ans=0,g[N]; void bfs(int s) { queue<int>q; d[s]=1; q.push(s); while(q.size()) { int x=q.front(); q.pop(); for(auto i:G[x]) { if(d[i]) continue; d[i]=d[x]+1; f[i][0]=x; for(int j=1;j<=t;j++) f[i][j]=f[f[i][j-1]][j-1]; q.push(i); } } } int lca(int x,int y) { if(d[x]>d[y]) swap(x,y); for(int i=t;i>=0;i--) if(d[f[y][i]]>=d[x]) y=f[y][i]; if(x==y) return x; for(int i=t;i>=0;i--) { if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; } return f[x][0]; } struct Edge { int u,v,l,dep,id; }e[K]; bool cmp(Edge a,Edge b) { return a.dep>b.dep||(a.dep==b.dep&&a.id<b.id); } int main() { // freopen("input.in","r",stdin); // freopen("output.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin>>n>>m>>k; t=log2(n+m)+1; for(int i=1;i<=n;i++) cin>>g[i],pos[i]=i; for(int i=1;i<=m;i++) { int u,v; cin>>u>>v; G[n+i].push_back(pos[u]); G[n+i].push_back(pos[v]); pos[v]=n+i; } for(int i=n+m;i>=1;i--) if(!d[i]) bfs(i); for(int i=1;i<=k;i++) { int u,v; cin>>u>>v; int LCA=lca(u,v); if(!LCA) continue; e[++tot]=Edge{u,v,LCA,d[LCA],i}; } sort(e+1,e+tot+1,cmp); for(int i=1;i<=tot;i++) { int s=min(g[e[i].u],g[e[i].v]); g[e[i].u]=max(0ll,g[e[i].u]-s),g[e[i].v]=max(0ll,g[e[i].v]-s); ans+=(s<<1); } cout<<ans; return 0; } ``` 如果觉得这篇题解写得好,请不要忘记点赞,谢谢!