1. 程式人生 > >[NOI2018]歸程

[NOI2018]歸程

[1] ont pbd eap 倍增 節點 合並 都是 font

題目大意:
一張n個點m條邊的無向圖,每條邊有權值和高度。每次詢問給出起點v和一個高度p,你在開始時可以花費0的價值走過高度大於p的邊,從第一次走過高度小於等於p的邊開始,走過一條邊要花費相應的權值。求走到1的最小花費。強制在線。
解題思路:
最短路跑Dijkstra即可(SPFA沒有了)。
如果可以離線,則並查集維護最大生成樹即可。強制在線的話,也可以可持久化並查集,帶兩只log。
正解是Kruskal重構樹。
我們在Kruskal的過程中,每次選中一條邊,合並兩個連通塊,都新建一個節點,然後把這個節點作為兩個連通塊的父親,高度設為邊的高度,距離則設為兩個兒子的距離的最小值。
如果高度大於這條邊(即這個節點),則兩邊的點都能直接走到,因此這個點的距離設為兒子節點距離的最小值。

這樣構造出來的樹有以下性質:
1. 這是一棵二叉樹。
2. 點從下往上高度遞減。
3. 原來的節點都是樹的葉子結點。
然後對於任意一個詢問,直接向上倍增,找到最上面一個高度大於p的,答案就是該節點的距離。
然後就只帶一只log辣,而且代碼很好寫。

C++ Code:

#include<bits/stdc++.h>
#include<ext/pb_ds/priority_queue.hpp>
typedef long long LoveLive;
const int N=200005;
inline int readint(){
    int c=getchar(),d=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
    d=(d<<3)+(d<<1)+(c^‘0‘);
    return d;
}
struct edge{
    int to,nxt,dis;
}e[N<<2];
struct kruskal_edge{
    int u,v,h;
    inline bool operator<(const kruskal_edge&rhs)const{return h>rhs.h;}
}ee[N<<1];
struct heap_node{
    LoveLive d;int u;
    inline bool operator<(const heap_node&rhs)const{return d>rhs.d;}
};
__gnu_pbds::priority_queue<heap_node>hp;
int n,m,head[N],cnt,fa[N<<1][21],nodes,ff[N<<1],H[N<<1];
LoveLive ans,d[N<<1];
bool vis[N];
inline int find(int x){return x==ff[x]?x:ff[x]=find(ff[x]);}
inline void addedge(int from,int to,int dis){
    e[++cnt]=(edge){to,head[from],dis};
    head[from]=cnt;
    e[++cnt]=(edge){from,head[to],dis};
    head[to]=cnt;
}
void dijkstra(){
    memset(d,0x3f,sizeof d);
    d[1]=0;
    hp.push((heap_node){0,1});
    memset(vis,0,sizeof vis);
    while(!hp.empty()){
        heap_node nw=hp.top();
        hp.pop();
        if(vis[nw.u])continue;
        vis[nw.u]=1;
        for(int i=head[nw.u];i;i=e[i].nxt)
        if(!vis[e[i].to]&&d[e[i].to]>d[nw.u]+e[i].dis){
            d[e[i].to]=d[nw.u]+e[i].dis;
            hp.push((heap_node){d[e[i].to],e[i].to});
        }
    }
}
int main(){
    for(int T=readint();T--;){
        ans=cnt=0;
        memset(e,0,sizeof e);
        memset(head,0,sizeof head);
        n=readint(),m=readint();
        for(int i=1;i<=m;++i){
            int u=readint(),v=readint(),l=readint(),a=readint();
            addedge(u,v,l);
            ee[i]=(kruskal_edge){u,v,a};
        }
        dijkstra();
        std::sort(ee+1,ee+m+1);
        for(int i=1;i<=n;++i)ff[i]=i,ff[i+n]=i+n;
        nodes=n;
        int less_node=n-1;
        memset(H,0,sizeof H);
        for(int i=1;less_node&&i<=m;++i){
            int x=find(ee[i].u),y=find(ee[i].v);
            if(x!=y){
                --less_node;
                ff[x]=ff[y]=fa[x][0]=fa[y][0]=++nodes;
                H[nodes]=ee[i].h;
                d[nodes]=std::min(d[x],d[y]);
            }
        }
        for(int j=1;j<21;++j)
        for(int i=1;i<=nodes;++i)
        fa[i][j]=fa[fa[i][j-1]][j-1];
        for(int Q=readint(),K=readint(),S=readint();Q--;){
            int v=(1ll*readint()+K*ans-1)%n+1,p=(1ll*readint()+K*ans)%(S+1);
            for(int j=20;~j;--j)if(H[fa[v][j]]>p)v=fa[v][j];
            printf("%lld\n",ans=d[v]);
        }
    }
    return 0;
}

[NOI2018]歸程