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;
    }
    
  • 相关阅读:
    ThroughRain第一次冲刺个人总结
    【操作系统】实验四 主存空间的分配和回收
    学习回顾与总结
    关于学术诚信与职业道德的承诺
    sprint个人总结+读书博客
    实验三 进程调度模拟程序
    团队项目3.0
    软件改进
    团队项目项目:二次开发
    作业调度
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LOJ2718.html
Copyright © 2011-2022 走看看