D148 Kruskal 重构树 P4768 [NOI2018] 归程
D148 Kruskal 重构树 P4768 [NOI2018] 归程_哔哩哔哩_bilibili
无向图,n 个点,m 条边,每条边有两个边权 长度 w 和 海拔 hi,q 次询问,每次询问给一个起点 v 和一个当天水位 p,
要求从 v 点回到 1 号点,只有当前边的海拔 hi 大于水位 p 才可以开车,一旦不满足条件必须步行,求步行的最小距离。
思路
如果 $v$ 到 $1$ 的最短路上的边都满足海拔 $h_i>p$,那么答案就是最短路
先跑 Dijkstra,预处理出 1 号点到每个点的最短路,再考虑海拔的约束条件
如果不满足条件,就要找出从 $v$ 出发只经过海拔 $h_i>p$ 的边能到达的哪些节点,怎样找?
Kruskal 重构树可以求原图上从 $x$ 出发只经过边权 $\ge val$ 的边能到达的节点数,并且这些点都在以某个新建点为根的子树中
我们以海拔为边权,先建 Kruskal 重构树,然后从 v 点开始,树上倍增,找到深度最小且 $h_i>p$ 的根节点
怎样求该节点到 1 号点的距离?
提前遍历一遍重构树,从根节点进入,自底向上更新,预处理出每个新建点到 1 号点的距离
图中,节点 $5,6,7$ 到 $1$ 号点的距离分别为 $1,0,0$。从 $4$ 出发,找海拔大于 $2$ 的点就是节点 $5$,$5$ 到 $1$ 号点的距离是 $1$

相关板子:
D146【模板】Kruskal 重构树 U201076 [Bzoj3732] Network - 董晓 - 博客园
// Kruskal 重构树 O(MlogM+NlogN) #include<bits/stdc++.h> #define pii pair<int,int> using namespace std; int read(){ int f=1,x=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return f*x; } const int N=800005; int idx,h[N],to[N],ne[N],ww[N]; //边权为长度 void add(int u,int v,int w){ to[++idx]=v;ww[idx]=w;ne[idx]=h[u];h[u]=idx; } struct E{int u,v,hi;}e[N]; //边权为海拔 int n,m,cnt,val[N]; //val新建点点权 int d[N],vis[N]; int pa[N],fa[N][20]; void dijkstra(){ //预处理1到所有节点的最短路 memset(vis,0,sizeof vis); memset(d,0x3f,sizeof d); d[1]=0; priority_queue<pii,vector<pii>,greater<pii> > q; //小根堆 q.push({0,1}); while(!q.empty()){ auto u=q.top().second; q.pop(); if(vis[u])continue; vis[u]=1; for(int i=h[u];i;i=ne[i]){ int v=to[i],w=ww[i]; if(d[v]>d[u]+w){ d[v]=d[u]+w; q.push({d[v],v}); } } } } int find(int x){ //并查集查找 return pa[x]==x?x:pa[x]=find(pa[x]); } void kruskal(){ //重构树 memset(h,0,sizeof(h)); idx=1; sort(e+1,e+1+m,[&](E a,E b){return a.hi>b.hi;}); for(int i=1;i<=n;++i)pa[i]=i; for(int i=1;i<=m;i++){ int fu=find(e[i].u), fv=find(e[i].v); if(fu!=fv){ val[++cnt]=e[i].hi; pa[fu]=pa[fv]=pa[cnt]=cnt; add(cnt,fu,0); add(cnt,fv,0); } } } void dfs(int u,int f){ //预处理 d,fa fa[u][0]=f; for(int i=1;i<=19;i++) fa[u][i]=fa[fa[u][i-1]][i-1]; for(int i=h[u];i;i=ne[i]){ int v=to[i]; dfs(v,u); d[u]=min(d[u],d[v]); //更新那些新建点到1的最短距离 } } int main(){ int T=read(); while(T--){ memset(h,0,sizeof(h)); idx=1; memset(fa,0,sizeof(fa)); memset(d,0x3f,sizeof(d)); n=read();m=read();cnt=n; //cnt新建点 for(int i=1;i<=m;i++){ int u=read(),v=read(),w=read(),hi=read(); add(u,v,w); add(v,u,w); e[i]={u,v,hi}; } dijkstra(); //预处理1到所有节点的最短路 kruskal(); //重构树 dfs(cnt,0); //预处理 d,fa int Q=read(),K=read(),S=read(); for(int last=0;Q--;){ int v=read(),p=read(); v=(v+K*last-1)%n+1; p=(p+K*last)%(S+1); for(int j=19;j>=0;--j) //倍增找到深度最小且海拔大于水位的节点 if(fa[v][j] && val[fa[v][j]]>p) v=fa[v][j]; printf("%lld\n",last=d[v]); } } }
浙公网安备 33010602011771号