P4768 [NOI2018] 归程

链接:P4768 [NOI2018] 归程

观察一下题目,如果没有车,求一个单源最短路就行了(但不要使用一种广为人知的最短路算法)

现在考虑有车的情况,显然最优策略是坐车到离1号节点最近的车能去的点下车。于是我们还是可以预处理每个点到1号节点的最短路,每次从节点v开始广搜它能去的所有海拔大于p的节点,时间复杂度O(Qn),瓶颈在于广搜

在回顾一下,我们所求的是离1号节点最近的海拔大于一个值的点,完全不需要用广搜维护。如果是离线可以带权并查集,但是这题在线。有没有一种树形结构满足权值单调呢,并且子树内的点都可以不依靠子树外的节点联通——Kruskal重构树完美满足。

我们按照边的海拔从大到小排序,建树,用下面的树演示一下如何计算答案。

image

cur是出发节点v的一个祖先,如果cur的海拔大于p,那么cur子树内的点都是联通的(车可以到达),因为权值单调,cur子树内的点肯定海拔也大于p

贪心地,cur子树内的点肯定越多越好,也就是说curv的祖先里离根节点最近的海拔大于p的节点。怎么找cur?用倍增跳就行了(别忘了权值单调)。找到了cur以后,cur子树内最小dis值(预处理的最短路)就是答案

现在只剩最后一个问题,怎么找一个子树内最小的dis值?

这很简单,反正建完树以后树又不会变,预处理dfs一下就行了。

最短路O(mlongm),kruskalO(mlogm),倍增O(NlongN)

ps:因为Kruskal重构树的节点数是N=2×n+1,所以不要忘了双倍空间

1|0CODE:

#include<bits/stdc++.h> #define int long long using namespace std; int t; int nows; int fa[800001]; int n,m; int q,k,s; int lastans; struct stu{ int from,to,val,h; bool operator<(const stu &kl)const{ return h>kl.h; } }edge[800001]; struct node{ int x,dis; bool operator<(const node &kl)const{ return dis>kl.dis; } }; int tree[800001],dp[800001][30],dep[800001],dis[800001],ans[800001]; bool vis[800001]; vector<node> nbr[800001]; vector<int> line[800001]; int find(int xx){ if(fa[xx]==xx) return xx; else return fa[xx]=find(fa[xx]); } void dijkstra(int s){ memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); dis[s]=0; node cur={s,dis[s]}; priority_queue<node> pq; pq.push(cur); while(!pq.empty()){ cur=pq.top(); pq.pop(); int x=cur.x; if(vis[x]==true) continue; vis[x]=true; for(int i=0;i<nbr[x].size();i++){ int y=nbr[x][i].x; int d=nbr[x][i].dis; if(dis[y]>dis[x]+d){ dis[y]=dis[x]+d; node nxt={y,dis[y]}; pq.push(nxt); } } } return ; } void kruskal(){ sort(edge+1,edge+1+m); nows=n; for(int i=1;i<=m;i++){ int x=find(edge[i].from); int y=find(edge[i].to); if(x!=y){ nows++; tree[nows]=edge[i].h; fa[x]=fa[y]=fa[nows]=nows; line[nows].push_back(x); line[nows].push_back(y); } } } void dfs(int now){ ans[now]=dis[now]; for(int i=0;i<line[now].size();i++){ dfs(line[now][i]); ans[now]=min(ans[now],ans[line[now][i]]); } return; } void get_lca(int now,int fa){ dep[now]=dep[fa]+1; dp[now][0]=fa; for(int i=1;(1<<i)<=dep[now];i++){ dp[now][i]=dp[dp[now][i-1]][i-1]; } for(int i=0;i<line[now].size();i++){ get_lca(line[now][i],now); } return; } int help(int now,int h){ for(int i=23;i>=0;i--){ if(dp[now][i] && tree[dp[now][i]]>h) now=dp[now][i]; } return ans[now]; } signed main(){ cin>>t; while(t--){ lastans=0; memset(tree,0xcf,sizeof(tree)); memset(ans,0x3f,sizeof(ans)); memset(dp,0,sizeof(dp)); for(int i=1;i<=n+m;i++){ nbr[i].clear(); line[i].clear(); } cin>>n>>m; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++){ cin>>edge[i].from>>edge[i].to>>edge[i].val>>edge[i].h; node fi={edge[i].to,edge[i].val}; nbr[edge[i].from].push_back(fi); fi={edge[i].from,edge[i].val}; nbr[edge[i].to].push_back(fi); } dijkstra(1); kruskal(); dfs(nows); get_lca(nows,0); cin>>q>>k>>s; while(q--){ int v0,p0; cin>>v0>>p0; int v=(v0+k*lastans-1)%n+1; int p=(p0+lastans*k)%(s+1); //cout<<v<<" "<<p<<endl; lastans=help(v,p); cout<<lastans<<endl; } } return 0; }

__EOF__

本文作者星河倒注
本文链接https://www.cnblogs.com/wangwenhan/p/17656257.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   星河倒注  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示