Kruskal 重构树

多用于在线解决:在一个图中只能走 //</>l 的边,从 u 出发,所能到达的点,针对它们进行的一系列询问。

Kruskal 重构树是基于 Kruskal MST 算法生成的一棵含有 n+m 个节点的点带权二叉树,有 n 个叶子,分别对应原 n 个节点;它可以让从 u 出发,只经过 l 的边的所有点集中在某个子树的所有叶子上,进而使用线段树等数据结构方便维护。

流程

初始时重构树中有 n 个节点,没有边,每个节点权值为 0。
将所有边从小到大排序,如 Kruskal MST 地遍历,判断两端 x,y 是否在一个并查集内,不在的话,新建一个节点 t(从 n+1 开始编号),将 x 所在连通块(子树)的根和 y 所在子树的根分别连向 t,并 fa[find(y)]=find(x),并将合并后的并查集的根重置为 t,将 t 的点权设为这条边的边权。

性质

父亲的点权大于等于儿子的点权。
两点之间的路径上边权最大值的最小值 = MST 中两点之间树边最大值 = 重构树中两点 lca 的点权
从一个点出发只经过小于等于某值的边能到达的所有点处在同一子树的所有叶子上。

针对第三点,如何定位这个子树?借助第一点,在 u 到根的链上倍增一个最浅的点使得它的点权是 l 的,它就是子树根。

如果是 ,排序的时候就从大到小来。

模板:[NOI2018]归程

复制
#include <bits/stdc++.h>
using namespace std;
inline int read(){
register char ch=getchar();register int x=0;
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
void print(int x){
if(x/10)print(x/10);
putchar(x%10+48);
}
const int N=2e5+5,INF=0x3f3f3f3f;
int n,m,q,K,S,nod,dfc,dis[N],fa[N*3][19],ma[N],rep[N*3],dfn[N*3],out[N*3],val[N*3],leaf[N],t[N<<2];
vector<pair<int,int> >G[N];
vector<int>T[N*3];
priority_queue<pair<int,int> >Q;
struct Edge {int u,v,l,a;}e[N*2];
int find(int x){return x==ma[x]?x:ma[x]=find(ma[x]);}
void unite(int x,int y){
x=find(x),y=find(y);
nod++;
T[nod].push_back(rep[x]);
T[nod].push_back(rep[y]);
//printf("[%d %d]\n[%d %d]\n",nod,rep[x],nod,rep[y]);
ma[y]=x;
rep[x]=nod;
}
void dfs(int x){
dfn[x]=dfc;
if(!T[x].size())dfc++,leaf[dfc]=x;
for(int i=1;i<=18;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
for(int y:T[x])fa[y][0]=x,dfs(y);
out[x]=dfc;
}
void pushup(int k){t[k]=min(t[k<<1],t[k<<1|1]);}
void build(int l,int r,int k){
if(l==r){t[k]=dis[leaf[l]];return;}
int mid=l+r>>1;
build(l,mid,k<<1),build(mid+1,r,k<<1|1);
pushup(k);
}
int ask(int L,int R,int l,int r,int k){
if(L<=l&&r<=R)return t[k];
int mid=l+r>>1,s=INF;
if(L<=mid)s=min(s,ask(L,R,l,mid,k<<1));
if(R>mid)s=min(s,ask(L,R,mid+1,r,k<<1|1));
return s;
}
void solve(){
n=read(),m=read();
for(int i=1;i<=n;i++)G[i].clear();
for(int i=1;i<=n+m;i++)T[i].clear();
dfc=0;
for(int i=1;i<=m;i++){
e[i].u=read(),e[i].v=read(),e[i].l=read(),e[i].a=read();
G[e[i].u].push_back(make_pair(e[i].v,e[i].l));
G[e[i].v].push_back(make_pair(e[i].u,e[i].l));
}
memset(dis,0x3f,sizeof dis),dis[1]=0,Q.push(make_pair(0,1));
while(!Q.empty()){
int x=Q.top().second;Q.pop();
for(pair<int,int>y:G[x]){
if(dis[y.first]>dis[x]+y.second){
dis[y.first]=dis[x]+y.second;
Q.push(make_pair(-dis[y.first],y.first));
}
}
}
sort(e+1,e+m+1,[](Edge a,Edge b){return a.a>b.a;});
nod=n;
for(int i=1;i<=n;i++)ma[i]=rep[i]=i,val[i]=INF;
for(int i=1;i<=m;i++)if(find(e[i].u)!=find(e[i].v)){
unite(e[i].u,e[i].v);
val[nod]=e[i].a;
}
dfs(nod);
build(1,n,1);
//for(int i=1;i<=n;i++)cout<<dfn[i]<<' '<<out[i]<<'\n';
q=read(),K=read(),S=read();
for(int v,p,las=0;q--;){
v=(read()+K*las-1)%n+1,p=(read()+K*las)%(S+1);
for(int i=18;~i;i--)if(fa[v][i]&&val[fa[v][i]]>p)v=fa[v][i];
print(las=ask(dfn[v]+1,out[v],1,n,1)),puts("");
}
}
int main(){
int T=read();
while(T--)solve();
}
posted @   pengyule  阅读(305)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
历史上的今天:
2021-07-12 割点割边强连通分量
点击右上角即可分享
微信分享提示