最小生成树

最小生成树为边权和最小的生成树

kruskal

贪心地,每次都加边权最小的边,如果这条边的两个端点已联通就不加

将两集合并入一个集合,查询两点是否在同一集合,用并查集维护

证明不会

重构树

当加入一条边时,建一个虚点当作这两点的父亲的父亲,点权为这条边的边权

重构树有重要性质:虚点点权从叶子到根非降或非升

P4768 [NOI2018] 归程

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define sf scanf
#define pf printf
#define mp make_pair
#define fo(i,x,y) for(register int i = x;i<=y;++i)
#define go(i,x,y) for(register int i = x;i>=y;--i)
using namespace std;
const int maxn = 200005;
const int maxm = 400005;
const int inf = 2147483647;
const int mod = 1000000007;

int T,n,m;

struct node1{
	int v,l,a;
};
vector<node1> e1[maxn];
void add1(int u,int v,int l,int a){
	e1[u].push_back(node1{v,l,a});
}
int dis[maxn];
bool vis[maxn];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;

struct node2{
	int u,v,l,a;
}e2[maxm];
bool cmp(node2 x,node2 y){
	return x.a > y.a;
}
int fa[maxn+maxn];
int getf(int x){
	return fa[x]<0?x:(fa[x]=getf(fa[x]));
}

int cnt_point;
vector<int> e3[maxn+maxn];
void add3(int u,int v){
	e3[u].push_back(v);
}
int stp[maxn+maxn][20],minv[maxn+maxn],dep[maxn+maxn],mind[maxn+maxn];

void clear_data(){
	memset(vis,0,sizeof vis);
	memset(dis,0x3f,sizeof(dis));
	dis[1] = 0;
	while(q.size()) q.pop();
	fo(i,1,400000) fa[i] = -1;
	fo(i,1,400000)
		while(e3[i].size())
			e3[i].pop_back();
	fo(i,1,200000)
		while(e1[i].size())
			e1[i].pop_back();
}
void read(){
	cin>>n>>m;
	fo(i,1,m){
		int u,v,l,a;
		cin>>u>>v>>l>>a;
		e2[i].u = u,e2[i].v = v,e2[i].l = l,e2[i].a = a;
		add1(u,v,l,a),add1(v,u,l,a);
	}
}
void dij(){
	fo(i,1,n) q.push(make_pair(dis[i],i));
	while(q.size()){
		pair<int,int> x = q.top();
		q.pop();
		int u = x.second,l = x.first;
		if(vis[u]) continue;
		vis[u] = true;
		for(node1 y:e1[u]){
			int v = y.v,ll = y.l;
			if(dis[v] > l+ll){
				dis[v] = l+ll;
				q.push(mp(dis[v],v));
			}
		}
	}
}
void kruscal(){
	cnt_point = n;
	sort(e2+1,e2+1+m,cmp);
	fo(i,1,m){
		int u = e2[i].u;
		int v = e2[i].v;
		int a = e2[i].a;
		int fu = getf(u),fv = getf(v);
		if(fu == fv) continue;
		cnt_point++;
		fa[cnt_point] = fa[fu]+fa[fv];
		fa[fu] = fa[fv] = cnt_point;
		add3(fu,cnt_point),add3(cnt_point,fu);
		add3(fv,cnt_point),add3(cnt_point,fv);
		minv[cnt_point] = a;
		mind[cnt_point] = inf;
	}
	fo(i,1,n)
		minv[i] = inf,
		mind[i] = dis[i];
}
void dfs(int now,int fa){
	dep[now] = dep[fa]+1;
	stp[now][0] = fa;
	fo(i,1,19)
		stp[now][i] = stp[stp[now][i-1]][i-1];
	for(auto v:e3[now]){
		if(v == fa) continue;
		dfs(v,now);
		mind[now] = min(mind[now],mind[v]);
	}
}
void init(){
	dij();
	kruscal();
	dfs(cnt_point,0);
}
int qq,k,s,st,p;
int query(int st,int p){
	go(i,19,0){
		if(dep[st] - (1<<i) > 0 and minv[stp[st][i]] > p)
			st = stp[st][i];
	}
	return mind[st];
}
void solve(){
	int lastans = 0;
	cin>>qq>>k>>s;
	while(qq--){
		cin>>st>>p;
		st = (st+k*lastans-1)%n + 1;
		p = (p+k*lastans)%(s+1);
		lastans = query(st,p);
		cout<<lastans<<"\n";
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--){
		clear_data();
		read();
		init();
		solve();
	}
}