kruskal 重构树

建图

kruskal重构树是个啥?
故名思意就是在跑kruskal的过程中重构出的一颗树
对于kruskal跑生成树时加进去的一条边(u,v)
我们在重构树中建立一个新节点t,连边(t,rt[u]),(t,rt[v])
t成为rt[u],rt[v]的父亲,rt[x]表示x在重构树中暂时的根节点(最后根节点都一样了嘛)

性质

  1. kruskal重构树是一颗二叉树
  2. 跑最小生成树形成的kruskal重构树是个大根堆
  3. 所有原图中的点在重构树中都是叶子节点
  4. 任意两点路径上边权的最大值为它们LCA的点权

货车运输

建立最大生成树,然后建这颗生成树的kruskal重构树
显然先用并查集判断两点是否联通,再直接拿重构树上两点lca的权值即路径中的最小权值

#include<bits/stdc++.h>
using namespace std;
const int maxn=10005,maxm=50005;
struct edge{
	int x,y,z;
	bool operator <(edge i)const{
		return z>i.z;
	}
}ed[maxm];
struct Edge{
	int to;
	int nxt;
}e[4*maxn];
int cnt;
int head[2*maxn];
int fat[2*maxn];
int val[2*maxn];
int dep[2*maxn],f[2*maxn][21];
bool vis[2*maxn];
int read(){
	int x=0,y=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')y=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*y;
}
int find(int x){
	if(fat[x]==x)return x;
	return fat[x]=find(fat[x]);
}
void add(int u,int v){
	e[cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt++;
	return ;
}
void dfs(int x,int fa){
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	for(int i=1;i<=20;i++)
		f[x][i]=f[f[x][i-1]][i-1];
	for(int i=head[x];i!=-1;i=e[i].nxt){
		int tmp=e[i].to;
		if(tmp==fa)continue;
		dfs(tmp,x);
	}
	return ;
}
int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[f[x][i]]>=dep[y])
			x=f[x][i];
	if(x==y)return x;
	for(int i=20;i>=0;i--)
		if(f[x][i]!=f[y][i]){
			x=f[x][i];
			y=f[y][i];
		}
	return f[x][0];
}
int main(){
	int n,m,q;
	n=read();m=read();
	for(int i=1;i<=2*n;i++)fat[i]=i;
	for(int i=1;i<=m;i++){
		int x,y,z;
		x=read();y=read();z=read();
		ed[i].x=x;ed[i].y=y;ed[i].z=z;
	}
	sort(ed+1,ed+m+1);
	int es=n;
	memset(head,-1,sizeof(head));
	for(int i=1;i<=m;i++){
		int u=ed[i].x,v=ed[i].y,w=ed[i].z;
		if(find(u)==find(v))continue;
		es++;
		val[es]=w;
		add(es,find(u));
		add(es,find(v));
		fat[find(u)]=fat[find(v)]=es;
	}
	for(int i=1;i<=n;i++){
		if(vis[find(i)])continue;
		vis[find(i)]=1;
		dfs(find(i),0);
	}
	q=read();
	for(int i=1;i<=q;i++){
		int x,y;
		x=read();y=read();
		if(find(x)!=find(y)){
			printf("-1\n");
			continue;
		}
		printf("%d\n",val[lca(x,y)]);
	}
	return 0;
}

Peaks

考虑建一颗最小生成树的重构树
建下来后,我们想找到所有v能到达的山峰中第k高的
所有v能到达的山峰一定在v的某一级祖先的整棵子树
那一级祖先满足的条件应是再上一级祖先的权值大于x
对于这一颗子树我们需要找第k大的点
考虑主席树,先将原重构树dfs序,然后子树一定在一个区间里
建立主席树,查询区间第k大

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=100005,maxm=500005,inf=0x3f3f3f3f;
struct node{
	int ls,rs;
	int num;
}tree[20*2*maxn];
int cur;
int root[2*maxn];
struct mountain{
	int id;
	int h;
	bool operator <(mountain i)const{
		return h<i.h;
	}
}a[maxn];
struct edge{
	int a,b,c;
	bool operator <(edge i)const{
		return c<i.c;
	}
}ed[maxm];
struct Edge{
	int to;
	int nxt;
}e[2*maxn];
int cnt;
int head[2*maxn];
int n,m,q,h;
int high[maxn];
int data[maxn];
int d[2*maxn];
int f[2*maxn];
int dfsnum=0;
int dfn[2*maxn];
int son[2*maxn];
int pre[2*maxn][21];
bool vis[2*maxn];
int read(){
	int x=0,y=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')y=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*y;
}
int find(int x){
	if(f[x]==x)return x;
	return f[x]=find(f[x]);
}
void add(int u,int v){
	e[cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt++;
	return ;
}
void modify(int x,int y,int l,int r,int val){
	if(l==r){
		tree[x].ls=tree[y].rs=0;
		tree[x].num=tree[y].num+1;
		return ;
	}
	int mid=l+((r-l)>>1);
	tree[x].num=tree[y].num+1;
	if(val<=mid){
		tree[x].ls=++cur;
		tree[x].rs=tree[y].rs;
		modify(tree[x].ls,tree[y].ls,l,mid,val);
	}
	else{
		tree[x].rs=++cur;
		tree[x].ls=tree[y].ls;
		modify(tree[x].rs,tree[y].rs,mid+1,r,val);
	}
	return ;
}
int query(int x,int y,int l,int r,int k){
	if(l==r)return data[l]; 
	int mid=l+((r-l)>>1);
	int ls1=tree[x].ls,rs1=tree[x].rs;
	int ls2=tree[y].ls,rs2=tree[y].rs;
	int num=tree[rs2].num-tree[rs1].num;
	if(tree[y].num-tree[x].num<k)return -1;
	if(num>=k)
		return query(rs1,rs2,mid+1,r,k);
	else
		return query(ls1,ls2,l,mid,k-num);
}
void dfs(int x,int fa){
	dfn[x]=++dfsnum;
	root[dfn[x]]=root[dfn[x]-1];
	if(x<=n){
		root[dfn[x]]=++cur;
		modify(root[dfn[x]],root[dfn[x]-1],1,h,high[x]);
	}
	son[x]=1;
	pre[x][0]=fa;
	for(int i=1;i<=20;i++)
		pre[x][i]=pre[pre[x][i-1]][i-1];
	for(int i=head[x];i!=-1;i=e[i].nxt){
		int tmp=e[i].to;
		dfs(tmp,x);
		son[x]+=son[tmp];
	}
	return ;
}
signed main(){
	n=read();m=read();q=read();
	for(int i=1;i<=n;i++){a[i].id=i;a[i].h=read();}
	sort(a+1,a+n+1);
	high[a[1].id]=++h;
	data[h]=a[1].h;
	for(int i=2;i<=n;i++){
		if(a[i].h>a[i-1].h)data[++h]=a[i].h;
		high[a[i].id]=h;
	}
	for(int i=1;i<=m;i++){
		int a,b,c;
		a=read();b=read();c=read();
		ed[i].a=a;ed[i].b=b;ed[i].c=c;
	}
	sort(ed+1,ed+m+1);
	int tot=n;
	for(int i=1;i<=2*n;i++)f[i]=i;
	memset(head,-1,sizeof(head));
	for(int i=1;i<=m;i++){
		int u=ed[i].a,v=ed[i].b,w=ed[i].c;
		u=find(u);
		v=find(v);
		if(u==v)continue;
		tot++;
		d[tot]=w;
		f[u]=f[v]=tot;
		add(tot,u);
		add(tot,v);
	}
	tree[0].ls=tree[0].rs=tree[0].num=0;
	for(int i=1;i<=tot;i++){
		if(vis[find(i)])continue;
		vis[find(i)]=1;
		dfs(find(i),0);
	}
	for(int i=1;i<=q;i++){
		int v,x,k;
		v=read();x=read();k=read();
		for(int j=20;j>=0;j--)
			if(pre[v][j]&&d[pre[v][j]]<=x)
				v=pre[v][j];
		printf("%lld\n",query(root[dfn[v]-1],root[dfn[v]+son[v]-1],1,h,k));
	}
	return 0;
}

[NOI2018]归程

这里我们对于每一个询问只能用单车跑海拔大于p的边,我们需要在所有能走这些边到达的点i中dis[i]最小的值
disi[i]表示从1到i的最短路
显然想到kruskal重构树
建立好最大生成树的重构树后,v点能去的点就是它的某级祖先的整棵子树里的所有点
这个祖先满足再上以及祖先的点权小于p
那么我们需要这一整棵子树的最小dis值
来一遍树形dp即能得到答案

#include<bits/stdc++.h>
using namespace std;
const int maxn=200005,maxm=400005;
const long long inf=0x3f3f3f3f3f3f3f3f;
struct edge{
	int u,v,a;
	bool operator <(edge i)const{
		return a>i.a;
	}
}ed[maxm];
struct que{
	int id;
	long long data;
	bool operator <(que i)const{
		return data>i.data;
	}
};
struct Edge{
	int to;
	long long w;
	int nxt;
}e[2*maxm];
int cnt;
int head[2*maxn];
int n,m;
int f[2*maxn];
long long dis[2*maxn];
long long val[2*maxn],dp[2*maxn];
int pre[2*maxn][21];
bool vis[2*maxn];
priority_queue<que>q;
int read(){
	int x=0,y=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')y=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*y;
}
void add(int u,int v,long long w){
	e[cnt].to=v;
	e[cnt].w=w;
	e[cnt].nxt=head[u];
	head[u]=cnt++;
	return ;
}
void dijkstra(){
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	que u;
	u.id=1;u.data=0;dis[1]=0;
	q.push(u);
	while(!q.empty()){
		int k=q.top().id;
		q.pop();
		if(vis[k])continue;
		vis[k]=1;
		for(int i=head[k];i!=-1;i=e[i].nxt){
			int tmp=e[i].to;
			if(dis[tmp]<=dis[k]+e[i].w)continue;
			dis[tmp]=dis[k]+e[i].w;
			que o;
			o.id=tmp;o.data=dis[tmp];
			q.push(o);
		}
	}
	return ;
}
int find(int x){
	if(f[x]==x)return x;
	return f[x]=find(f[x]);
}
void dfs(int x,int fa){
//	cout<<"ORZ"<<fa<<" "<<n<<endl;
	pre[x][0]=fa;
	for(int i=1;i<=20;i++)
		pre[x][i]=pre[pre[x][i-1]][i-1];
	if(x<=n)dp[x]=dis[x];
	else dp[x]=inf;
	for(int i=head[x];i!=-1;i=e[i].nxt){
		int tmp=e[i].to;
		dfs(tmp,x);
		dp[x]=min(dp[x],dp[tmp]);
	}
	return ;
}
int main(){
	int t;
	t=read();
	while(t--){
		n=read();m=read();
		cnt=0;
		memset(head,-1,sizeof(head));
		for(int i=1;i<=m;i++){
			int u,v,l,a;
			u=read();v=read();l=read();a=read();
			add(u,v,l);
			add(v,u,l);
			ed[i].u=u;ed[i].v=v;ed[i].a=a;
		}
		dijkstra();
		cnt=0;
		memset(head,-1,sizeof(head));
		sort(ed+1,ed+m+1);
		int tot=n;
		for(int i=1;i<=2*n;i++)f[i]=i;
		for(int i=0;i<=n;i++)val[i]=0;
		for(int i=1;i<=m;i++){
			int u=ed[i].u,v=ed[i].v,a=ed[i].a;
			if(find(u)==find(v))continue;
			tot++;
			val[tot]=a;
			add(tot,find(u),0);
			add(tot,find(v),0);
			f[find(u)]=f[find(v)]=tot;
		}
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=tot;i++){
			if(vis[find(i)])continue;
			vis[find(i)]=1;
			dfs(find(i),0);
		}
		int lastans=0;
		int Q,K,S;
		Q=read();K=read();S=read();
		for(int i=1;i<=Q;i++){
			int v,p;
			v=(read()+K*lastans-1)%n+1;
			p=(read()+K*lastans)%(S+1);
			for(int j=20;j>=0;j--)
				if(val[pre[v][j]]>p)
					v=pre[v][j];
//			cout<<"**"<<val[v]<<" "<<p<<endl;
			lastans=dp[v];
			printf("%lld\n",dp[v]);
		}
	}
	return 0;
}