【洛谷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;
}
posted @ 2020-12-04 08:04  stoorz  阅读(157)  评论(0编辑  收藏  举报