Kruskal 重构树
重构树#
如何构造#
先把边从小到大排序
在 合并两个点 所在的集合时,新建一个节点 同时连接 ,且 的点权等于 之间的边权
如这样一个图
我们先把边权从小到大排序
合并 时,新建点权为 的节点 ,连接 ,
合并 时,新建点权为 的节点 ,连接 ,
合并 时,新建点权为 的节点 ,连接 ,
最后构造出的 重构树 如下:
若有 个点,这样构造一棵 重构树的时间复杂度是 的
性质#
-
树上除叶子结点以外的点都对应着原来图中的边,叶子结点就是原来图上的节点
-
从每个点到根节点上除叶子结点外按顺序访问到的点的点权是单调的
-
出于 算法贪心的性质,两个点 和 的 的点权就对应着它们最小生成树上的最大值,也是原图中两个点之间的所有简单路径上最大边权的最小值
-
这棵树是一个二叉堆
习题#
P4768 归程#
题意:
个点, 条边,保证图联通,每条边有两个权值,一个长度 ,一个海拔
多组询问,告诉你起点和水位线,小于等于水位线的边都会被淹没,只能走路,否则可以开车
问从当天起点到1号节点最少步行经过的长度,有些询问会强制在线
思路:
从 到 最小步行的长度 ,前提是 到 的路上海拔全大于
那么可以先跑一边从 到其他所有点的最短路
注意这里要用 因为 死了
然后问题就转变为求 存在一条到 的路径海拔均大于 的点 组成的集合中 的最小值
很显然可以用 重构树了
我们先按照边的海拔值从大到小排序,然后跑一遍 重构树
这样对于树内的每一个节点 ,他的所有祖先的海拔值自下而上递减
并且对于每一个节点 记录 表示其子树内到 距离的最小值
搞一下倍增,找到 最上的祖先 使得 且
这样 的子树内都是可以通过 开车到达的点
即为答案
这样时间复杂度是
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
#define pb push_back
#define mp make_pair
#define pii pair <int,int>
#define int long long
const int N=6e5+5;
const int M=1e6+5;
const int inf=1e18;
struct edge{
int x,y,l,a,id;
bool operator < (const edge X) const{
return a>X.a;
}
}e[M];
struct node{
int x,d;
bool operator < (const node X) const{
return d>X.d;
}
};
int n,m,rt;
int q,k,s,lastans;
int f[N][20],g[N],son[N][2];
int dep[N],v[N],d[N];
bool vis[N];
vector <pii> G[N];
inline void dijkstra(){
priority_queue <node> q;
for(int i=2;i<=2*n;++i) d[i]=inf,vis[i]=0;
d[1]=0;
q.push({1,0});
while(!q.empty()){
int x=q.top().x;
q.pop();
vis[x]=1;
for(auto y:G[x]){
int to=y.first,val=y.second;
if(vis[to]) continue;
if(d[to]>d[x]+val){
d[to]=d[x]+val;
q.push({to,d[to]});
}
}
}
}
inline int get(int x){
return x==g[x]?x:g[x]=get(g[x]);
}
inline void kruskal(){
int num=0;
sort(e+1,e+m+1);
for(int i=1;i<=2*n;++i) g[i]=i;
for(int i=1;i<=m;++i){
int x=e[i].x,y=e[i].y,l=e[i].l,a=e[i].a,id=e[i].id;
int fx=get(x),fy=get(y);
if(fx==fy) continue;
g[fy]=g[fx]=f[fx][0]=f[fy][0]=++rt;
son[rt][0]=fx,son[rt][1]=fy;
f[rt][0]=0;
v[rt]=a;
++num;
if(num==n-1) break;
}
}
inline int dfs(int x,int fa){
dep[x]=dep[fa]+1;
for(int i=1;i<=19;++i) f[x][i]=f[f[x][i-1]][i-1];
if(son[x][0]) d[x]=min(dfs(son[x][0],x),d[x]);
if(son[x][1]) d[x]=min(dfs(son[x][1],x),d[x]);
return d[x];
}
inline int query(int x,int y){
for(int i=19;i>=0;--i)
if(dep[x]>(1<<i)&&v[f[x][i]]>y) x=f[x][i];
return d[x];
}
inline void solve(){
memset(f,0,sizeof(f));
memset(son,0,sizeof(son));
n=read(),m=read();rt=n;
for(int i=1;i<=n;++i) G[i].clear();
for(int i=1;i<=m;++i){
int x=read(),y=read(),l=read(),a=read();
e[i]=(edge){x,y,l,a,i};
G[x].pb(mp(y,l));
G[y].pb(mp(x,l));
}
dijkstra();
kruskal();
dfs(rt,0);
q=read(),k=read(),s=read();
lastans=0;
while(q--){
int x=read(),y=read();
x=(k*lastans+x-1)%n+1,y=(k*lastans+y)%(s+1);
lastans=query(x,y);
printf("%lld\n",lastans);
}
}
signed main(){
int T=read();
while(T--)
solve();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
2020-07-06 浅谈树状数组