zoukankan      html  css  js  c++  java
  • 【LOJ2718】「NOI2018」归程(Kruskal重构树)

    点此看题面

    大致题意: 给定一个无向图,每条边有一个长度以及一个海拔。多组询问,每次给定起点以及一个限制(h),要求从起点出发,先开车走海拔大于等于(h)的边到达某一节点,然后步行到达(1)号点。求最短的步行路程。

    关于(SPFA),它已经死了

    (Kruskal)重构树

    对于这种题目,我们首先以海拔为关键字建出(Kruskal)重构树,然后有一个重要结论:

    (x)出发只经过边权大于等于/小于等于(v)的边所能到达的点集,就是(x)深度最小点权大于等于(v)/小于等于(v)的祖先子树内所有的叶节点。

    实际求解时可以利用中点权的单调性倍增上跳(O(logn))求出这个祖先。

    解题思路

    根据先前提到的重要结论,我们可以(O(logn))求出从起点出发开车能到达的点集,而答案就是这些点到(1)号点的最短路的最小值。

    而每个点到(1)号点的最短路可以事先(Dijkstra)预处理。

    只要对于(Kruskal)重构树上的每个点维护一下子树内叶节点最短路的最小值即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200000
    #define M 400000
    #define LN 20
    #define add(x,y,v) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=v)
    using namespace std;
    int n,m,ee,lnk[N+5];struct edge {int to,nxt,val;}e[2*M+5];
    struct line {int x,y,h;I bool operator < (Con line& o) Con {return h>o.h;}}s[M+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,E=(C=FO)+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    namespace DJ//Dijkstra预处理最短路
    {
    	#define mp make_pair
    	#define fi first
    	#define se second
    	typedef pair<int,int> Pr;priority_queue<Pr,vector<Pr>,greater<Pr> > q;
    	int dis[N+5],vis[N+5];I void Dijkstra()
    	{
    		RI i,t;Pr k;for(i=1;i<=n;++i) dis[i]=2e9,vis[i]=0;q.push(mp(dis[1]=0,1));W(!q.empty())
    		{
    			if(k=q.top(),q.pop(),vis[k.se]) continue;
    			for(vis[k.se]=1,i=lnk[k.se];i;i=e[i].nxt)
    				(t=dis[k.se]+e[i].val)<dis[e[i].to]&&(q.push(mp(dis[e[i].to]=t,e[i].to)),0);
    		}
    	}
    }
    class KruskalTree//Kruskal重构树
    {
    	private:
    		int rt,V[N<<1],H[N<<1],S[N<<1][2],f[N<<1][LN+5];
    		int fa[N<<1];I int getfa(CI x) {return fa[x]^x?fa[x]=getfa(fa[x]):x;}//并查集
    		I void Init(CI x)//预处理
    		{
    			RI i;for(i=1;i<=LN;++i) f[x][i]=f[f[x][i-1]][i-1];if(x<=n) return (void)(V[x]=DJ::dis[x]);//叶节点直接赋权
    			f[S[x][0]][0]=f[S[x][1]][0]=x,Init(S[x][0]),Init(S[x][1]),V[x]=min(V[S[x][0]],V[S[x][1]]);//递归子树,上传信息
    		}
    		I int Jump(RI x,CI v) {for(RI i=LN;~i;--i) H[f[x][i]]>v&&(x=f[x][i]);return x;}//倍增上跳
    	public:
    		I void Build(line *s)//Kruskal算法
    		{
    			
    			RI i,x,y;for(i=1;i<=2*n-1;++i) fa[i]=i;for(sort(s+1,s+m+1),rt=n,i=1;i<=m;++i)
    				(x=getfa(s[i].x))^(y=getfa(s[i].y))&&(H[++rt]=s[i].h,fa[S[rt][0]=x]=fa[S[rt][1]=y]=rt);//建树
    			f[rt][0]=0,Init(rt);
    		}
    		I int Q(CI x,CI v) {return V[Jump(x,v)];}//询问,返回子树内叶节点最短路的最小值
    }K;
    int main()
    {
    	RI Tt,Qt,i,x,v,op,Mx,lst;F.read(Tt);W(Tt--)
    	{
    		for(F.read(n),F.read(m),ee=0,i=1;i<=n;++i) lnk[i]=0;//清空
    		for(i=1;i<=m;++i) F.read(s[i].x),F.read(s[i].y),
    			F.read(v),F.read(s[i].h),add(s[i].x,s[i].y,v),add(s[i].y,s[i].x,v);
    		DJ::Dijkstra(),K.Build(s);//预处理
    		F.read(Qt),F.read(op),F.read(Mx),lst=0;W(Qt--)
    			F.read(x),F.read(v),op&&(x=(x+lst-1)%n+1,v=(v+lst)%(Mx+1)),F.writeln(lst=K.Q(x,v));//询问
    	}return F.clear(),0;
    }
    
  • 相关阅读:
    Java 基础
    Java 数据类型
    Spring 拦截器实现事物
    SSH 配置日记
    Hibernate 知识提高
    Jsp、Servlet
    leetcode 97. Interleaving String
    leetcode 750. Number Of Corner Rectangles
    leetcode 748. Shortest Completing Word
    leetcode 746. Min Cost Climbing Stairs
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LOJ2718.html
Copyright © 2011-2022 走看看