P4768-归程(kruskal重构树+倍增+dijkstra预处理+dfs建立fa[][]时预处理哪个子节点最近)

题目
在这里插入图片描述N<=2e5 M<=4e5 Q<=4e5;

思路:从点v出发走海拔超过p的边。就是kruskal重构树了。先dijksta处理每个节点到出发点1的最短路d[].
然后dfs建立fa[][]数组时顺便求出每个节点所掌管的子节点以及他自己 离出发点1最近的节点。这里有些要注意。
if(d[son[y]]<d[son[x]]) son[x]=son[y]…记住这个把。。。

//P4768归程(kruskal重构树倍增+dijkstra预处理+dfs时预处理哪个子节点最近) 
//q次询问: 给点u,p u只能跑跑边长>p的边 只能走路 问回到1点最少走多少? N<=2e5 M<=4e5 Q<=4e5; 
#include<bits/stdc++.h>
#define m(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long ll;
const int N=2e5+5,M=4e5+5,K=20;
const ll INF=(ll)(1)<<(ll)(61);
struct _Edge{int u,v,l,w;}e[M];
inline int cmp(_Edge x,_Edge y){return x.w>y.w;}
struct Edge{int to,nex;}edge[N<<1];int head[N<<1],tot;
inline void add(int from,int to){
    edge[++tot]=(Edge){to,head[from]};head[from]=tot;
}
struct Edg{int to,len,nex;}edg[M<<1];int hed[N],ttt;
inline void ad(int from,int to,int len){
    edg[++ttt]=(Edg){to,len,hed[from]};hed[from]=ttt;
    edg[++ttt]=(Edg){from,len,hed[to]};hed[to]=ttt;
}
int f[N<<1],val[N],node;
inline int seek(int x){return (x==f[x])?x:(f[x]=seek(f[x]));}
int n,m;
void ex_kruskal(){
    for(int i=1;i<=n;++i) f[i]=i;
    sort(e+1,e+m+1,cmp);
    int cnt=0;node=n;
    for(int i=1;i<=m;++i){
        int u=e[i].u,v=e[i].v,fu,fv;
        fu=seek(u),fv=seek(v);
        if(fu==fv) continue;
        val[(++node)-n]=e[i].w,++cnt;
        f[node]=node,f[fu]=node,f[fv]=node;
        add(node,fu),add(node,fv);
        if(cnt==(n-1)) break;
    }
}
priority_queue<pair<int,int> >q;ll d[N<<1];
void dijkstra(int s){
    for(int i=1;i<=2*n;++i) d[i]=INF;//也要初始化[n+1,2*n].
    d[s]=0,q.push(make_pair(0,s));
    while(!q.empty()){
        int x=q.top().second;q.pop();
        for(int i=hed[x];i;i=edg[i].nex){
            int y=edg[i].to,l=edg[i].len;
            if(d[y]>d[x]+l){d[y]=d[x]+l,q.push(make_pair(-d[y],y));}
        }
    }
}
int dep[N<<1],fa[N<<1][K+5],son[N<<1];
void dfs(int x){
    for(int j=1;(1<<j)<=dep[x];j++) fa[x][j]=fa[fa[x][j-1]][j-1];
    son[x]=x;
    for(int i=head[x];i;i=edge[i].nex){
        int y=edge[i].to;
        dep[y]=dep[x]+1,fa[y][0]=x;
        dfs(y);
        if(d[son[y]]<d[son[x]]) son[x]=son[y];
    }
}
inline int BZ(int x,ll w){
    for(int j=K;j>=0;j--)
        if(fa[x][j]!=-1&&val[fa[x][j]-n]>w) x=fa[x][j];
    return x;
}
void init(){
    m(head,0),tot=0,m(hed,0),ttt=0;
    m(fa,-1);
    for(int i=1;i<=2*n;++i) son[i]=i;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);init();
        for(int i=1;i<=m;++i) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].w),ad(e[i].u,e[i].v,e[i].l);
        ex_kruskal();
		dijkstra(1);fa[node][0]=node,dep[node]=0,dfs(node);
        int q,k,s;scanf("%d%d%d",&q,&k,&s);ll las=0;
        while(q--){
            int v;ll p;scanf("%d%lld",&v,&p);
            v=(v+k*las-1)%n+1,p=(p+k*las)%(s+1);
            v=BZ(v,p);printf("%lld\n",las=d[son[v]]);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值