zoukankan      html  css  js  c++  java
  • NOI2018Day1T1 归程 并查集 kruskal kruskal重构树 倍增表 Dijkstra

    原文链接https://www.cnblogs.com/zhouzhendong/p/NOI2018Day1T1.html 

    题目传送门 - 洛谷P4768

    题意

      给定一个无向连通图,有 $n$ 个点 $m$ 条边,每条边有两个属性:海拔$(a)$、距离$(l)$。

      有 $Q$ 组询问,每组询问两个数 $v,p$,表示询问从点 $v$ 出发,从第一次走海拔高度不超过 $p$ 的边起算,问行走距离最小为多少。(即,在第一次走海拔高度不超过 $p$ 的边之前,走的所有边都是免费的)

      $T$ 组数据,强制在线。

      $1leq Tleq 3, nleq 2 imes 10^5, mleq 4 imes 10^5, Qleq 4 imes 10^5, a,pleq 10^9, lleq 10^4, 1leq vleq n$ 

    题解

      洛谷老爷机貌似非常慢,比€€F老爷机慢。

      我们先把问题转化一下。

      预处理出点 $1$ 到每一个点的最短路长度 $dis$。

        这个东西还好我用了堆优化的 Dijkstra 。后来听说: 关于 SPFA                                           它死了

      每一次询问,就是:连接海拔高度大于 $p$ 的所有边,求 $v$ 能到达的点中的最小 $dis$ 值。

      首先考虑离线做法。

      我们按照海拔从高到低依次加边,用 kruskal 的做法生成森林。维护一下连通块的最小 $dis$ 值,然后顺便询问就可以了。

      但是强制在线。

      1.  可持久化并查集 $Longrightarrow$ 可能会被卡常数。

      2.  Kruskal 重构树 + 倍增。

      我们令合并时的新节点权值为当前海拔,然后预处理祖先倍增表。

      每次询问,倍增到深度最小的海拔大于 $p$ 的节点,输出子树最小 $dis$ 值即可。

      作为同步赛选手写出来了,但是本机测大样例最后一个点 $1.26s$ 。放到 UOJ 上面自定义测试一下 , $0.183s$ ……

      事实证明€€F老爷机跑的还是挺快的,期望得分:100,实际得分:100。

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    #include <cmath>
    #include <vector>
    #include <queue>
    using namespace std;
    int read(){
    	char ch=getchar();
    	int x=0;
    	while (!('0'<=ch&&ch<='9'))
    		ch=getchar();
    	while ('0'<=ch&&ch<='9')
    		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return x;
    }
    const int N=200005,M=400005;
    int T,n,m;
    struct Gragh{
    	int cnt,y[M*2],z[M*2],nxt[M*2],fst[N];
    	void clear(){
    		cnt=0;
    		memset(fst,0,sizeof fst);
    	}
    	void add(int a,int b,int c){
    		y[++cnt]=b,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt;
    	}
    }g;
    struct Edge{
    	int x,y,h;
    	Edge(){}
    	Edge(int _x,int _y,int _h){
    		x=_x,y=_y,h=_h;
    	}
    	friend bool operator < (Edge a,Edge b){
    		return a.h>b.h;
    	}
    }e[M];
    int dis[N],vis[N];
    struct Node{
    	int x,d;
    	Node(){}
    	Node(int _x,int _d){
    		x=_x,d=_d;
    	}
    	friend bool operator < (Node x,Node y){
    		return x.d>y.d;
    	}
    };
    priority_queue <Node> Q;
    void Dijkstra(){
    	while (!Q.empty())
    		Q.pop();
    	for (int i=1;i<=n;i++)
    		dis[i]=2e9+5;
    	dis[1]=0;
    	memset(vis,0,sizeof vis);
    	Q.push(Node(1,0));
    	while (!Q.empty()){
    		Node now=Q.top();
    		Q.pop();
    		int x=now.x;
    		if (vis[x])
    			continue;
    		vis[x]=1,dis[x]=now.d;
    		for (int i=g.fst[x];i;i=g.nxt[i])
    			Q.push(Node(g.y[i],dis[x]+g.z[i]));
    	}
    }
    const int N2=N*2;
    int fa[N2],son[N2][2],h[N2],mindis[N2],Fa[N2][20];
    int getf(int x){
    	return fa[x]==x?x:fa[x]=getf(fa[x]);
    }
    int Qu[N2],head,tail;
    int main(){
    //	freopen("return.in","r",stdin);
    //	freopen("return.out","w",stdout);
    	T=read();
    	while (T--){
    		n=read(),m=read();
    		g.clear();
    		for (int i=1;i<=m;i++){
    			int x=read(),y=read(),l=read(),a=read();
    			g.add(x,y,l);
    			g.add(y,x,l);
    			e[i]=Edge(x,y,a);
    		}
    		Dijkstra();
    		sort(e+1,e+m+1);
    		memset(fa,0,sizeof fa);
    		for (int i=1;i<=n*2;i++)
    			fa[i]=i;
    		for (int i=1;i<=n;i++)
    			h[i]=e[1].h+1;
    		int cnt=n;
    		memset(son,0,sizeof son);
    		memset(h,0,sizeof h);
    		for (int i=1;i<=m;i++){
    			int x=getf(e[i].x),y=getf(e[i].y);
    			if (x==y)
    				continue;
    			h[++cnt]=e[i].h;
    			son[cnt][0]=x,son[cnt][1]=y;
    			fa[x]=fa[y]=cnt;
    		}
    		head=tail=0;
    		Qu[++tail]=cnt;
    		while (head<tail){
    			int x=Qu[++head];
    			for (int i=1;i<19;i++)
    				Fa[x][i]=Fa[Fa[x][i-1]][i-1];
    			for (int i=0;i<2;i++){
    				int y=son[x][i];
    				if (y){
    					Fa[y][0]=x;
    					Qu[++tail]=y;
    				}
    			}
    		}
    		for (int i=tail;i>0;i--){
    			int x=Qu[i];
    			if (x<=n)
    				mindis[x]=dis[x];
    			else
    				mindis[x]=min(mindis[son[x][0]],mindis[son[x][1]]);
    		}
    		int q=read(),K=read(),S=read();
    		int lastans=0;
    		h[0]=-1;
    		while (q--){
    			int v=read(),p=read();
    			v=(v+K*lastans-1)%n+1;
    			p=(p+K*lastans)%(S+1);
    			for (int i=18;i>=0;i--)
    				if (h[Fa[v][i]]>p)
    					v=Fa[v][i];
    			printf("%d
    ",lastans=mindis[v]);
    		}
    	}
    //	fclose(stdin);
    //	fclose(stdout);
    	return 0;
    }
    

      

  • 相关阅读:
    git merge远程合并
    开发中必知必会的常用Linux命令
    mysql双机双向热备
    入门级实操教程!从概念到部署,全方位了解K8S Ingress!
    linux常用命令,你应该了解的Linux知识
    MFC的静态库.lib、动态库.dll(包含引入库.lib)以及Unicode库示例
    Java 表达式之谜:为什么 index 增加了两次?
    Vavr Option:Java Optional 的另一个选项
    一文详解 Java 的八大基本类型!
    如何找到真正的 public 方法
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/NOI2018Day1T1.html
Copyright © 2011-2022 走看看