「NOI2018」归程 (Kruskal 重构树/持久化并查集)
「NOI2018」归程 (Kruskal 重构树/持久化并查集)
题意:每次查询仅通过边权\(\leq k\)能够到达的点中,距离根最近的
离线做法:直接并查集维护当前\(\leq k\)边权的情况
强制在线当然可以直接可持久化并查集
持久化并查集非常麻烦,但是我们这里不需要进行回退操作,所以不需要可持久化
Kruskal重构树:按照边权检出生成树,每次合并两个点就新建一个点向他们连边,最后得到的点就是根
构建的重构树边权是单调的,每次能够到达的点是一个子树,倍增维护二分,子树预处理答案
const int N=2e5+10,M=4e5+10;
int n,m;
struct Edge{
int to,nxt,w;
}e[M<<1];
int head[N],ecnt;
void AddEdge(int u,int v,int w){
e[++ecnt]=(Edge){v,head[u],w};
head[u]=ecnt;
}
struct DijNode{
int x,d;
bool operator < (const DijNode __) const {
return d>__.d;
}
};
priority_queue <DijNode> que;
int dis[N];
void Dijkstra(){
rep(i,1,n) dis[i]=2e9;
que.push((DijNode){1,dis[1]=0});
while(!que.empty()) {
DijNode now=que.top(); que.pop();
int u=now.x;
if(now.d>dis[u]) continue;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].to,w=e[i].w;
if(dis[v]>dis[u]+w) dis[v]=dis[u]+w,que.push((DijNode){v,dis[v]});
}
}
}
struct NODE{
int u,v,w;
bool operator < (const NODE __) const {
return w<__.w;
}
} E[M];
int bfa[N<<1],fa[20][N<<1],mi[N<<1],W[N<<1];
int Find(int x){ return x==bfa[x]?x:bfa[x]=Find(bfa[x]); }
int q,S,k,tn;
int main(){
//freopen("return.in","r",stdin),freopen("return.out","w",stdout);
rep(kase,1,rd()) {
n=rd(),m=rd();
rep(i,1,n) head[i]=0; ecnt=0;
rep(i,1,m) {
int u=rd(),v=rd(),l=rd(),a=rd();
E[i]=(NODE){u,v,a};
AddEdge(u,v,l);
AddEdge(v,u,l);
}
Dijkstra();
rep(i,1,n*2) bfa[i]=i,fa[0][i]=0;
rep(i,1,tn=n) mi[i]=dis[i];
sort(E+1,E+m+1);
drep(i,m,1) {
int x=E[i].u,y=E[i].v,w=E[i].w;
x=Find(x),y=Find(y);
if(x==y) continue;
fa[0][x]=fa[0][y]=bfa[x]=bfa[y]=++n;
mi[n]=min(mi[x],mi[y]);
W[n]=w;
}
rep(i,1,18) rep(j,1,n) fa[i][j]=fa[i-1][fa[i-1][j]];
q=rd(),k=rd(),S=rd();
int lst=0;
rep(i,1,q) {
int x=(rd()+k*lst-1)%tn+1,p=(rd()+k*lst)%(S+1);
drep(j,18,0) if(fa[j][x] && W[fa[j][x]]>p) x=fa[j][x];
printf("%d\n",lst=mi[x]);
}
}
}