• 【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;
    }
    
  • 相关阅读:
    Windows下做7层软负载方案分析
    蛙蛙推荐:C语言入门之二——编写第一个有意义的小程序
    蛙蛙推荐:C语言入门之一——Linux下的C开发环境搭建小节
    蛙蛙推荐:动手做个网页游戏五子棋
    学习园地:微博项目
    com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field 异常 规格严格
    PostgreSQL Update 根据B表更新A表 规格严格
    SQL 删除重复数据[转] 规格严格
    ntoskrnl.exe占用cpu高 规格严格
    PostgreSQL中RECURSIVE递归查询使用总结[转] 规格严格
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LOJ2718.html
Copyright © 2020-2023  润新知