LGP4768 [NOI 2018] 归程 学习笔记

LGP4768 [NOI 2018] 归程 学习笔记

Luogu Link

题意简述

给定一个 \(n\)\(m\) 边的无向连通图。每条边有两个权值 \(w,z\)

\(q\) 次询问,每次给定 \(s,p\),你要选择一个点 \(t\) 并最小化 \(\sum_{e\in E(t\to 1)}w_e\),其中 \(t\) 需要满足 \(\exist E(s\to t),\forall e\in E,z_e>p\)(说人话就是:)。强制在线。

\(n\le 2\times 10^5,m\le 4\times 10^5,q\le 4\times 10^5\)

时限 \(\text{4.00s}\)

做法解析

这里给出一种使用 Kruskal 重构树的做法。OI-wiki相关条目

为什么是 Kruskal 重构树?因为这题实际上做的事情就是,对于每个询问我们要找到所有和它以“路径上最小边权不小于 \(p\)”这一条件连通的点——这个形式太典型了。

按照 \(z\) 建一棵小根 Kruskal 重构树。这样,对于每个询问,任意边结点 \(u\) 只要满足 \(z_u>p\) 就意味着其子树内所有点都可以无代价互通。所以我们用倍增找到最浅的 \(s\) 的祖先 \(a\) 满足 \(z_a>p\),答案就是 \(\min_{v\in \text{subtree}(a)}\text{dis}(v\to 1)\)

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=4e5+5,MaxNb=23;
int N,M,X,Y,W,Z,Q,Typ,S;
struct edge{int u,v,w,z;}E[MaxN];
int getv(edge &e,int x){return e.u==x?e.v:e.u;}
vector<int> Gr[MaxN];
void addedge1(int u,int v,int w,int z,int i){
    E[i]={u,v,w,z};
    Gr[X].push_back(i);
    Gr[Y].push_back(i);
}
uint dis[MaxN];
struct anod{
    int u;uint d;
    friend bool operator>(anod a,anod b){return a.d>b.d;}
};
priority_queue<anod,vector<anod>,greater<anod> > pq;
void dijkstra(int s){
    fill(dis,dis+N*2,Ihu7f);
    dis[s]=0,pq.push({s,0});
    while(pq.size()){
        auto [u,d]=pq.top();
        pq.pop();if(d>dis[u])continue;
        for(int x : Gr[u]){
            auto e=E[x];
            int v=getv(e,u),w=e.w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                pq.push({v,dis[v]});
            }
        }
    }
}
int K[MaxN],kfa[MaxN][MaxNb];
vector<int> Kr[MaxN];uint ans[MaxN],fans;
void addedge2(int c,int u,int v){
    Kr[c].push_back(u);
    Kr[c].push_back(v);
}
struct UnionFind{
    int n,ufa[MaxN];
    void init(int x){n=x;for(int i=1;i<=n;i++)ufa[i]=i;}
    int find(int u){return ufa[u]==u?u:ufa[u]=find(ufa[u]);}
    void mudge(edge e){
        auto [u,v,w,z]=e;
        int fu=find(u),fv=find(v);
        if(fu==fv)return;
        n++,ufa[fu]=ufa[fv]=ufa[n]=n;
        addedge2(n,fu,fv),K[n]=z;
    }
}UniFd;
bool cmpz(edge a,edge b){return a.z>b.z;}
void dfs(int u){
    ans[u]=dis[u];
    for(auto v : Kr[u]){
        kfa[v][0]=u;
        dfs(v),minner(ans[u],ans[v]);
    }
}
void kruskal(){
    UniFd.init(N);sort(E+1,E+M+1,cmpz);
    for(int i=1;i<=M;i++)UniFd.mudge(E[i]);
    dfs(UniFd.n);
    for(int i=1;i<=log2(N*2);i++){
        for(int u=1;u<N*2;u++){
            kfa[u][i]=kfa[kfa[u][i-1]][i-1];
        }
    }
}
uint solve(int u,int lim){
    for(int i=log2(N*2);i>=0;i--)if(K[kfa[u][i]]>lim)u=kfa[u][i];
    return ans[u];
}
void befinit(){
    K[0]=-1,fans=0;
    for(int i=1;i<N*2;i++)Kr[i].clear(),Gr[i].clear();
}
void mian(){
    readis(N,M),befinit();
    for(int i=1;i<=M;i++){
        readis(X,Y,W,Z);
        addedge1(X,Y,W,Z,i);
    }
    dijkstra(1),kruskal();
    readis(Q,Typ,S);
    for(int i=1;i<=Q;i++){
        readis(X,Z);
        X=(X+Typ*fans-1)%N+1;
        Z=(Z+Typ*fans)%(S+1);
        fans=solve(X,Z);
        writil(fans);
    }
}
int Tcn;
int main(){
    readi(Tcn);
    while(Tcn--)mian();
    return 0;
}
posted @ 2025-05-03 20:55  矞龙OrinLoong  阅读(7)  评论(0)    收藏  举报