zoukankan      html  css  js  c++  java
  • 【洛谷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
    ",last=binary(u,p));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    C# 使用HtmlAgilityPack抓取网页信息
    企业微信开发之向员工付款(C#)
    企业微信开发之发放企业红包(C#)
    body-parser的使用
    NodeJs的express服务器报413 payload too large错误
    'nuxt' 不是内部或外部命令,也不是可运行的程序
    Linux node使用npm成功安装命令后,执行时却报找不到命令的问题
    npm查看全局安装了那些包的命令
    JS中==与===的区别
    JS事件流理解
  • 原文地址:https://www.cnblogs.com/stoorz/p/14083869.html
Copyright © 2011-2022 走看看