題解:歸程

我們可以把從v到1的路徑分成兩部分,一半全開車,一半全走路

也就是說要枚舉n個節點作爲斷點(假設當前斷點爲u),這個斷點是可行解與最優解當且僅當存:在一條從v到u的路徑可以全部開車且從u到1全部走路的最短路是滿足上一條件中最短的

從v出發開車可以到的點, 一定滿足路徑上所有邊海拔都高於水位

Kruskal重構樹:

這裏已經很明顯可以用Kruskal重構樹求解了

我們把每條邊按海拔降序排序,重構樹完成後,對於每次詢問,找到樹中深度最小且海拔大於水位的節點,那麼他的子樹的全部節點都可以由v開車到達

這一點可以由重構樹是小根堆的性質簡單得證,即每個節點子樹內所有節點海拔都比該節點大

現在要求的最後一步就是這個子樹內所有節點到1號節點的步行最短路, 因爲是無向圖,所以一開始預處理1節點到所有節點最短路就好,然後dfs可以順便求出某個節點子樹內的最短路

--

\(ps\)
這個題卡 \(SPFA\) CCF怎麼能這樣對我可愛的SPFA
所以必須打迪傑斯特拉


處理細節:

  1. 莫名卡常(可能是我不優秀),對於一些數組不用清零的就別清了,比如倍增數組和最小值數組(刪了HSYOJ就過了)
  2. 克魯斯卡爾重構樹套路:清零!!!(數據不清空,爆零兩行淚)

可持久化並查集

一定程度上,可持久化並查集的適用範圍是大於克魯斯卡爾重構樹的,雖然在這裏不是最優秀的做法,把\(NlogN\)的複雜度硬生生變成了\(Nlog^2N\)(卡卡常也是能過的),這裏雖然不是正解,但也是一個好方案

做法:

迪傑斯特拉預處理,然後可持久化並查集維護聯通塊(口胡完了)

實現細節:

注意常數


代碼:

(這裏用的是Kruskal重構樹)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;

#define ll long long
#define re register
#define gc getchar()
inline int read()
{
    re int x(0),f(1);re char ch(gc);
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=gc;}
    while(ch>='0'&&ch<='9') x=(x*10)+(ch^48),ch=gc;
    return x*f;
}

const int N=8e5+10;
int h[N],cnt,n,m,f[N][22],tot;
ll val[N],_min[N],dis[N];

struct edge{int next,to;ll w;}e[N<<1];
void add(int u,int v,ll w){e[++cnt]=(edge){h[u],v,w},h[u]=cnt;} 
#define QXX(u) for(int i=h[u],v;v=e[i].to,i;i=e[i].next)

struct node{int u,v;ll h;}E[N<<1];
bool operator < (node a,node b){return a.h>b.h;}

struct Node{ll x;int id;};
bool operator < (Node a,Node b){return a.x<b.x;}
priority_queue <Node> q;
int vis[N];

int fa[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}

void djs()
{
    memset(vis,0,sizeof(vis));
    memset(dis,127,sizeof(dis));
    q.push((Node){0,1});
    dis[1]=0;
    while(!q.empty())
    {
        Node t=q.top();q.pop();
        int u=t.id;
        if(vis[u]) continue;
        vis[u]=1;
        QXX(u)
            if(dis[u]+e[i].w<dis[v])
            {
                dis[v]=dis[u]+e[i].w;
                q.push((Node){-dis[v],v});
            }
        
    }
}
void dfs(int u)
{
    _min[u]=dis[u];
    QXX(u)
    {
        f[v][0]=u;
        dfs(v);
        _min[u]=min(_min[u],_min[v]);
    }
}
void kruskal()
{
    memset(h,0,sizeof(h));
    cnt=0;
    sort(E+1,E+1+m);
    for(int i=1;i<=n;++i) fa[i]=i;
    for(int i=1;i<=m;++i)
    {
        int u=find(E[i].u),v=find(E[i].v);
        if(u!=v)
        {
            val[++tot]=E[i].h;
            fa[u]=fa[v]=fa[tot]=tot;
            add(tot,u,0),add(tot,v,0);
        }
    }
    dfs(tot);
}
int main()
{
//  freopen("return.in","r",stdin);
    int T=read();
    while(T--)
    {
        memset(h,0,sizeof(h));
        memset(f,0,sizeof(f));
        memset(_min,127,sizeof(_min));
        n=read(),m=read(),cnt=0;tot=n;
        for(int i=1;i<=m;++i)
        {
            re int u=read(),v=read(),w=read(),h=read();
            add(u,v,w),add(v,u,w);
            E[i].u=u,E[i].v=v,E[i].h=h;
        }
        djs();
        kruskal();
        for(int i=1;(1<<i)<=tot;i++)
            for(int u=1;u<=tot;u++)
                f[u][i]=f[f[u][i-1]][i-1];
        int Q=read(),K=read(),S=read();
        ll la=0;
        while(Q--) 
        {
            int vi=read(),pi=read();
            vi=(vi+K*la-1)%n+1;
            pi=(pi+K*la)%(S+1);
            for(int j=21;j>=0;--j)
                if(f[vi][j]&&val[f[vi][j]]>pi) 
                    vi=f[vi][j];
            la=_min[vi];
            cout<<la<<endl;
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章