题目信息

传送门

题解

65%

很容易想到离线做法:

将边按权值从高到低排序,丢进并查集处理。同时将询问离线,按水位线从高到低。

并查集维护当前水位下没有积水的连通块,以及块中离1号点最近的距离。当然,还得跑个单源最短路来求距离。由于水位线是在递减,并查集不会有删除操作,说明这样做是正确的。

然而这题强制在线……

100%

我们发现并查集处理不了这道题

但是

可持久化并查集可以……

可持久化并查集可以用【可持久化数组+并查集】理解,可持久化数组可以用【可持久化线段树】理解,可持久化线段树也就是非权值线段树版【主席树】。于是正解的思路就出来了。

同65%的加边操作,只是每条边加完以后记录版本。查询时访问对应的版本,求出点所在连通块到1号点的最小距离。

不过这题恶心的地方在代码和一些细节。调了2个小时,如果真在考场上肯定写完65%走人。

注意:

  • 海拔需要离散
  • 并查集不能路径压缩,不然空间会爆。需要按秩合并。按秩合并相当于启发式合并,深度小的合并到深度大的上。这里又会引出一个细节——当两个根深度相同,需要人为给合并后的那个深度加一以保证正确性。

我这里更新后的块内最小距离是记录到根编号上,比较神秘。据说开个真的并查集,用可持久化数组记录变化要快很多,有兴趣的可以尝试一下。

3.47K AC:

#include <bits/stdc++.h>
#define mp make_pair
#define mem(O) memset(O,0,sizeof(O))
using namespace std;
typedef long long ll;
typedef pair<int,int> P;

inline char nc(){
    #ifdef nijisanji
        return getchar();
    #endif
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int red(){
    int x=0,f=1;char ch=nc();for(;!isdigit(ch);ch=nc())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=nc())x=x*10+ch-'0';return x*f;
}

const int N = 5e5+10;

int n,m,p;
int lis[N];

struct edge{int u,v,w,h,nxt;}e[N<<1],E[N];
int head[N],add_cnt;
inline void add(int u,int v,int w){e[++add_cnt]=(edge){u,v,w,0,head[u]};head[u]=add_cnt;}
bool cmp(edge a,edge b){return a.h>b.h;}

int rt[N],fa[N<<5],dep[N<<5];ll mn[N<<5];

namespace SHR{
    ll dis[N];int vis[N];
    void dijkstra(){
        memset(dis,0x3f,sizeof(dis));
        priority_queue <P,vector<P>,greater<P> > q;
        dis[1]=0,q.push(mp(dis[1],1));
        while(!q.empty()){
            int u=q.top().second;q.pop();
            if(vis[u])    continue;vis[u]=1;
            for(int i=head[u];i;i=e[i].nxt){
                int v=e[i].v;
                if(dis[v]>dis[u]+e[i].w){
                    dis[v]=dis[u]+e[i].w;
                    q.push(mp(dis[v],v));
                }
            }
        }
    }
}

namespace HJT{
    int sz,ls[N<<5],rs[N<<5];
    #define mid (tl+tr>>1)
    void build(int &o,int tl,int tr){
        o=++sz;
        if(tl==tr)    return fa[o]=tl,mn[o]=SHR::dis[tl],dep[o]=0,void();
        build(ls[o],tl,mid),build(rs[o],mid+1,tr);
    }
    void herige(int &o,int per,int tar,int tl,int tr){
        o=++sz;
        ls[o]=ls[per],rs[o]=rs[per];
        if(tl==tr){fa[o]=fa[per],dep[o]=dep[per],mn[o]=mn[per];return;}
        if(tar<=mid)    herige(ls[o],ls[per],tar,tl,mid);
        else            herige(rs[o],rs[per],tar,mid+1,tr);
    }
    void merge(int &o,int per,int tar,int tl,int tr,int f){
        o=++sz;
        ls[o]=ls[per],rs[o]=rs[per];
        if(tl==tr){fa[o]=f,dep[o]=dep[per],mn[o]=mn[per];return;}
        if(tar<=mid)    merge(ls[o],ls[per],tar,tl,mid,f);
        else            merge(rs[o],rs[per],tar,mid+1,tr,f);
    }
    void update(int o,int tar,int tl,int tr){
        if(tl==tr)    return ++dep[o],void();
        if(tar<=mid)    update(ls[o],tar,tl,mid);
        else            update(rs[o],tar,mid+1,tr);
    }
    int query(int o,int tar,int tl,int tr){
        if(tl==tr)    return o;
        if(tar<=mid)    return query(ls[o],tar,tl,mid);
        else            return query(rs[o],tar,mid+1,tr);
    }
    #undef mid
}

namespace UDF{
    int find(int o,int tar){
        int u=HJT::query(o,tar,1,n);
        if(fa[u]==tar)    return u;
        return find(o,fa[u]);
    }
    void merge(int x,int y,int i){
        int fx=find(rt[i],x),fy=find(rt[i],y);
        if(fa[fx]!=fa[fy]){
            if(dep[fx]>dep[fy])    swap(fx,fy);
            HJT::merge(rt[i],rt[i-1],fa[fx],1,n,fa[fy]);
            HJT::herige(rt[i],rt[i],fa[fy],1,n);
            int py=HJT::query(rt[i],fa[fy],1,n);
            int px=HJT::query(rt[i],fa[fx],1,n);
            mn[py]=min(mn[py],mn[px]);
            if(dep[fx]==dep[fy])    HJT::update(rt[i],fa[fy],1,n);
        }
    }
}

void discrete(){
    sort(E+1,E+m+1,cmp);
    for(int i=1;i<=m;++i)    lis[i]=E[m-i+1].h;
}

ll lastans=0;

void solve(){
    n=red(),m=red();
    for(int i=1;i<=m;++i){
        int u=red(),v=red(),l=red(),a=red();
        add(u,v,l),add(v,u,l);
        E[i]=(edge){u,v,l,a,0};
    }
    SHR::dijkstra();
    discrete();
    HJT::build(rt[0],1,n);
    for(int i=1;i<=m;++i){
        rt[i]=rt[i-1];
        UDF::merge(E[i].u,E[i].v,i);
    }
    ll Q=red(),K=red(),S=red();
    for(int i=1;i<=Q;++i){
        ll u=(red()+K*lastans-1)%n+1;
        ll pl=(red()+K*lastans)%(S+1);
        int ver=m-(upper_bound(lis+1,lis+m+1,pl)-lis)+1;
        lastans=mn[UDF::find(rt[ver],u)];
        printf("%lld\n",lastans);
    }
}

void clear(){
    mem(SHR::vis),mem(rt),mem(head);
    add_cnt=0,HJT::sz=0,lastans=0;
}

int main(){
    int T=red();
    while(T--){
        solve();
        if(T) clear();
    }
    return 0;
}