【题解】[省选联考 2021 A/B 卷] 宝石

sol:
本人绞尽脑汁在想只带一个 log 的算法 (可惜没有想出来)

最后还是只能二分 233 …

核心思想 : 倍增 + 二分答案。

对于左边部分路径,由于起点是固定的,可以直接倍增搞定。

这时候右边部分路径的起点就是 x (我们通过上一个步骤求出来的答案)

其实左半部分和右半部分的求法本质不同(这个可以自行探究233,因为前者是固定了一个端点呀)

考虑二分答案。这个时候相当于固定了终点,可以反过来求最远的起点,判断两条路径能否拼起来即可。

最后的问题在于如何求出 父亲节点中第一个宝石为 i 的位置 。直接暴力存路径显然空间会爆炸,可以用主席树维护(主席树维护静态区间问题 真香)。

时间复杂度 O(nlogn+qlog^2n) 。原题的值域略小(但是对本解法影响不大)。

#include<bits/stdc++.h> using namespace std; const int Maxn=2e5+5; int n,m,q,c,a[Maxn],p[Maxn],num; int f1[Maxn][20],f2[Maxn][20]; int f[Maxn][20],dep[Maxn]; int lson[Maxn*20],rson[Maxn*20],dian[Maxn*20],tot,rt[Maxn]; vector<int> g[Maxn]; //倍增 + 二分答案 (倍增 + 二分是核心) //每次就找 +1 / -1 的点倍增地往上跳 //剩下唯一一个找 pre /suf 的操作,可以用主席树维护 //这样由于二分多一个 log ,时间复杂度 O(nlogn + qlog^2n) void build(int &p,int q,int l,int r,int x,int y) { p=++tot; lson[p]=lson[q]; rson[p]=rson[q]; if(l==r) { dian[p]=y; return; } int mid=l+r>>1; if(x<=mid) build(lson[p],lson[q],l,mid,x,y); else build(rson[p],rson[q],mid+1,r,x,y); } int qry(int p,int l,int r,int x) { if(l==r) { return dian[p]; } int mid=l+r>>1; if(x<=mid) return qry(lson[p],l,mid,x); else return qry(rson[p],mid+1,r,x); } int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=19;i>=0;i--) { if(dep[f[x][i]]>=dep[y]) x=f[x][i]; } if(x==y) return x; for(int i=19;i>=0;i--) { if(f[x][i]!=f[y][i]) { x=f[x][i],y=f[y][i]; } } return f[x][0]; } void dfs(int x,int fa) { f[x][0]=fa; dep[x]=dep[fa]+1; for(int i=1;i<20;i++) { f[x][i]=f[f[x][i-1]][i-1]; } build(rt[x],rt[fa],0,c,a[x],x); if(a[x]) { f1[x][0]=qry(rt[x],0,c,a[x]-1); for(int i=1;i<20;i++) { f1[x][i]=f1[f1[x][i-1]][i-1]; } } if(a[x]<c) { f2[x][0]=qry(rt[x],0,c,a[x]+1); for(int i=1;i<20;i++) { f2[x][i]=f2[f2[x][i-1]][i-1]; } } for(auto y:g[x]) { if(y==fa) continue; dfs(y,x); } } int main() { scanf("%d%d%d",&n,&m,&c); for(int i=1;i<=c;i++) { int x; scanf("%d",&x); p[x]=i; } for(int i=1;i<=n;i++) { scanf("%d",&a[i]); a[i]=p[a[i]]; } for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u); } dfs(1,0); scanf("%d",&q); for(int i=1;i<=q;i++) { int u,v; scanf("%d%d",&u,&v); int lc=lca(u,v); int x=qry(rt[u],0,c,1); int tmp=0; if(dep[x]>dep[lc]) { tmp++; for(int j=19;j>=0;j--) { if(dep[f2[x][j]]>dep[lc]) { x=f2[x][j]; tmp+=1<<j; } } } int l=tmp+1,r=c,ans=tmp; while(l<=r) { int mid=l+r>>1; int x=qry(rt[v],0,c,mid),tot=1; if(dep[x]<dep[lc]) { r=mid-1; continue; } for(int j=19;j>=0;j--) { if(dep[f1[x][j]]>=dep[lc]) { x=f1[x][j]; tot+=1<<j; } } if(tmp+tot>=mid) { ans=mid; l=mid+1; } else { r=mid-1; } } printf("%d\n",ans); } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530231.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示