「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]);
		}
	}
}

posted @ 2020-04-18 09:21  chasedeath  阅读(126)  评论(0编辑  收藏  举报