【洛谷P4768】归程
题目
题目链接:https://www.luogu.com.cn/problem/P4768
本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定。
魔力之都可以抽象成一个 \(n\) 个节点、\(m\) 条边的无向连通图(节点的编号从 \(1\) 至 \(n\))。我们依次用 \(l,a\) 描述一条边的长度、海拔。
作为季风气候的代表城市,魔力之都时常有雨水相伴,因此道路积水总是不可避免的。由于整个城市的排水系统连通,因此有积水的边一定是海拔相对最低的一些边。我们用水位线来描述降雨的程度,它的意义是:所有海拔不超过水位线的边都是有积水的。
Yazid 是一名来自魔力之都的 OIer,刚参加完 ION2018 的他将踏上归程,回到他温暖的家。Yazid 的家恰好在魔力之都的 \(1\) 号节点。对于接下来 \(Q\) 天,每一天 Yazid 都会告诉你他的出发点 \(v\) ,以及当天的水位线 \(p\)。
每一天,Yazid 在出发点都拥有一辆车。这辆车由于一些故障不能经过有积水的边。Yazid 可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。
需要特殊说明的是,第二天车会被重置,这意味着:
- 车会在新的出发点被准备好。
- Yazid 不能利用之前在某处停放的车。
Yazid 非常讨厌在雨天步行,因此他希望在完成回家这一目标的同时,最小化他步行经过的边的总长度。请你帮助 Yazid 进行计算。
思路
不难想到按照每条路的海拔建出 kruskal 重构树,其中子节点比父节点的海拔更高。这样每次询问时开车可以到的点集就是一棵子树。
然后我们只需要求在这棵子树内的所有点距离 \(1\) 号节点最短的是哪一个就可以了。所以直接从 \(1\) 号点开始跑 dij,然后 dfs 求一下每棵子树内距离最小的即可。
至于一次询问如何找到可以走的子树,直接倍增找就可以了。
注意 SPFA 死了这部经典就是出自这道题,所以求最短路请不要使用 SPFA。
时间复杂度 \(O((m+Q)\log n)\)。
代码
#include <bits/stdc++.h>
#define mp make_pair
using namespace std;
const int N=800010,LG=20;
int T,Q,n,m,tot,val[N],dis[N],head[N],father[N],f[N][LG+1];
bool vis[N];
struct edge1
{
int u,v,dis;
}e1[N];
struct edge2
{
int next,to,dis;
}e2[N];
bool cmp(edge1 x,edge1 y)
{
return x.dis>y.dis;
}
void add(int from,int to,int dis)
{
e2[++tot]=(edge2){head[from],to,dis};
head[from]=tot;
}
void prework()
{
memset(head,-1,sizeof(head));
tot=0;
}
int find(int x)
{
return x==father[x]?x:father[x]=find(father[x]);
}
void dij()
{
for (int i=1;i<=2*n;i++)
dis[i]=2e9+10,vis[i]=0;
priority_queue<pair<int,int> > q;
q.push(mp(0,1)); dis[1]=0;
while (q.size())
{
int u=q.top().second; q.pop();
if (vis[u]) continue;
vis[u]=1;
for (int i=head[u];~i;i=e2[i].next)
{
int v=e2[i].to;
if (dis[v]>1LL*dis[u]+e2[i].dis)
{
dis[v]=dis[u]+e2[i].dis;
q.push(mp(-dis[v],v));
}
}
}
}
void kruskal()
{
sort(e1+1,e1+1+m,cmp);
for (int i=1;i<=n*2;i++) father[i]=i;
int num=n;
for (int i=1;i<=m;i++)
{
int x=find(e1[i].u),y=find(e1[i].v);
if (x!=y)
{
num++; val[num]=e1[i].dis;
add(num,x,0); add(num,y,0);
father[x]=father[y]=num;
}
}
}
void dfs(int x,int fa)
{
f[x][0]=fa;
for (int i=1;i<=LG;i++)
f[x][i]=f[f[x][i-1]][i-1];
if (!val[x]) val[x]=val[fa];
for (int i=head[x];~i;i=e2[i].next)
{
dfs(e2[i].to,x);
dis[x]=min(dis[x],dis[e2[i].to]);
}
}
int binary(int x,int p)
{
for (int i=LG;i>=0;i--)
if (val[f[x][i]]>p) x=f[x][i];
return dis[x];
}
int main()
{
scanf("%d",&T);
while (T--)
{
prework();
scanf("%d%d",&n,&m);
for (int i=1,d;i<=m;i++)
{
scanf("%d%d%d%d",&e1[i].u,&e1[i].v,&d,&e1[i].dis);
add(e1[i].u,e1[i].v,d); add(e1[i].v,e1[i].u,d);
}
dij();
prework();
kruskal();
for (int i=1;i<=n*2;i++)
if (find(i)==i) dfs(i,0);
int s,k,u,p,last=0;
scanf("%d%d%d",&Q,&k,&s);
while (Q--)
{
scanf("%d%d",&u,&p);
u=(u+k*last-1)%n+1; p=(p+k*last)%(s+1);
printf("%d\n",last=binary(u,p));
}
}
return 0;
}