链接

https://loj.ac/problem/2718

思路

我们希望x所在的连通块尽量的大,而且尽量走高处

离线的话可以询问排序,kruskal过程中更新答案

在线就要用kruskal重构树

这kruskal重构树的话,看图就明白了

叶子节点都是原树节点

非叶子节点都是边

按照从大到小的顺序依次加边(是深度不是长度)

如果连通块已经在一起就不联通,其他两个最大节点和这个边(新建节点)连边

看图就是很明白



我们发现,重构树的根到任意节点是单调的,也就是说,这是个二叉堆啊

那两点间联通的最小需要深度就是lca(x,y)这条边的深度

询问就是s这个点最远能向上跳到的最远点的子树答案

可能我讲的听不懂,那就看图自己想想吧

复杂度最多一个log

大体流程

多组数据,进入solve

dij预处理dis

kruskal利用并查集重构出树来

lca树剖预处理重构树

再dfs统计子树ans

之后在线查询lca

输出lca这个点的ans

细节

并查集最好优化下,因为他重构出来的树很容易感觉成哈夫曼树的形状

并查集按秩合并和随机rand时间差别不大

树剖链外跳,链内二分

错误

写了比较慢、、(不过一遍60TLE还是很嗨的)

错误只有一个,dij的堆忘记了默认大顶堆,导致复杂度不对,T死了

代码

/*
大体流程:
多组数据,进入solve
dij预处理dis
kruskal重构出树来
lca树剖预处理重构树(其实就是二分)
再dfs统计子树ans
之后在线查询
*/
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <utility>
#include <algorithm>
using namespace std;
const int N=1e6+7;
int read() {
int x=0,f=1;char s=getchar();
for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
return x*f;
}
int n,m,rt;
int dis[N],w[N<<1],dsr[N<<1];
struct node {
int u,v,l,a;
bool operator < (const node &b) const {
return a>b.a;
}
} edge[N];
struct noi_2018_t1 {
int v,nxt;
} e[N<<1];
int head[N<<1],add_tot;
void add(int u,int v) {
// cout<<u<<" "<<v<<"\n";
e[++add_tot].v=v;
e[add_tot].nxt=head[u];
head[u]=add_tot;
}
namespace bcj {
int fa[N<<1],siz[N<<1],id[N<<1];
int find(int x) {
return fa[x]==x ? x : fa[x]=find(fa[x]);
}
void uu(int fx,int fy) {
fx=find(fx),fy=find(fy);
id[fx]=id[fy]=max(id[fx],id[fy]);
if(siz[fx]<=siz[fy]) {
fa[fx]=fy;
if(siz[fx]==siz[fy]) siz[fy]++;
} else {
fa[fy]=fx;
}
}
}
namespace get_lca {
int fa[N<<1],top[N<<1],siz[N<<1],son[N<<1],dep[N<<1],idx[N<<1],js,frm[N<<1];
void dfs1(int u,int f) {
dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int topf) {
top[u]=topf;
idx[u]=++js;
frm[js]=u;
if(!son[u]) return;
dfs2(son[u],topf);
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(!idx[v]) dfs2(v,v);
}
}
int lca(int x,int val) {
int ans=x;
while(w[top[x]]>val) ans=top[x],x=fa[top[x]];
int l=idx[top[x]],r=idx[x];
while(l<=r) {
// cout<<l<<" "<<r<<"\n";
int mid=(l+r)>>1;
if(w[frm[mid]]>val) ans=frm[mid],r=mid-1;
else l=mid+1;
}
return ans;
}
void init() {
dfs1(rt,0);
dfs2(rt,rt);
}
}
namespace dij {
struct node {
int v,nxt,q;
}e[N<<1];
int head[N<<1],tot;
void add(int u,int v,int q) {
e[++tot].v=v;
e[tot].q=q;
e[tot].nxt=head[u];
head[u]=tot;
}
void dij() {
priority_queue<pair<int,int> > q;
q.push(make_pair(0,1));
dis[1]=0;
while(!q.empty()) {
pair<int,int> u=q.top();
q.pop();
if(dis[u.second]!=-u.first) continue;
for(int i=head[u.second];i;i=e[i].nxt) {
int v=e[i].v;
if(dis[v]>dis[u.second]+e[i].q) {
dis[v]=dis[u.second]+e[i].q;
q.push(make_pair(-dis[v],v));
}
}
}
}
void work() {
memset(dis,0x3f,sizeof(dis));
memset(head,0,sizeof(head));
tot=0;
for(int i=1;i<=m;++i) {
add(edge[i].u,edge[i].v,edge[i].l);
add(edge[i].v,edge[i].u,edge[i].l);
}
dij();
}
}
namespace kruskal {
void work() {
for(int i=1;i<n+n;++i) bcj::fa[i]=i;
for(int i=1;i<n+n;++i) bcj::id[i]=i;
sort(edge+1,edge+1+m);
for(int i=1;i<=n;++i) w[i]=0x3f3f3f3f;
for(int i=1,js=n;i<=m;++i) {
int fx=bcj::find(edge[i].u),fy=bcj::find(edge[i].v);
if(fx!=fy) {
rt=++js;
add(js,bcj::id[fx]),add(js,bcj::id[fy]);
w[js]=edge[i].a;
bcj::uu(js,fy);
bcj::uu(js,fx);
if(js==n+n-1) return;
}
}
}
}
void clear() {
add_tot=0;
dij::tot=0;
get_lca::js=0;
memset(dij::head,0,sizeof(dij::head));
memset(get_lca::siz,0,sizeof(get_lca::siz));
memset(bcj::siz,0,sizeof(bcj::siz));
memset(head,0,sizeof(head));
memset(dis,0x3f,sizeof(dis));
memset(get_lca::idx,0,sizeof(get_lca::idx));
memset(get_lca::son,0,sizeof(get_lca::son));
}
void dfs(int u,int f) {
dsr[u]=dis[u];
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
dfs(v,u);
dsr[u]=min(dsr[u],dsr[v]);
}
}
void solve() {
dij::work();
// cout<<"1\n";
kruskal::work();
// cout<<"2\n";
get_lca::init();
// cout<<"3\n";
dfs(rt,0);
int Q=read(),k=read();
int S=read(),lastans=0;
while(Q--) {
int vv=read(),pp=read();
int v=(vv+k*lastans-1)%n+1;
int p=(pp+k*lastans)%(S+1);
int tmp=get_lca::lca(v,p);
lastans=dsr[tmp];
printf("%d\n",lastans);
}
}
int main() {
freopen("return.in","r",stdin);
freopen("return.out","w",stdout);
int T=read();
while(T--) {
n=read(),m=read();
for(int i=1;i<=m;++i) {
edge[i].u=read(),edge[i].v=read();
edge[i].l=read(),edge[i].a=read();
}
clear();
solve();
}
return 0;
}

#2718. 「NOI2018」归程 kruskal重构树的更多相关文章

  1. loj2718 「NOI2018」归程[Kruskal重构树+最短路]

    关于Kruskal重构树可以翻阅本人的最小生成树笔记. 这题明显裸的Kruskal重构树. 然后这题限制$\le p$的边不能走,实际上就是要满足走最小边权最大的瓶颈路,于是跑最大生成树,构建Krus ...

  2. loj#2718. 「NOI2018」归程

    题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...

  3. LOJ #2718. 「NOI2018」归程(Dijkstra + Kruskal重构树 + 倍增)

    题意 给你一个无向图,其中每条边有两个值 \(l, a\) 代表一条边的长度和海拔. 其中有 \(q\) 次询问(强制在线),每次询问给你两个参数 \(v, p\) ,表示在 \(v\) 出发,能开车 ...

  4. 洛谷 4768 LOJ 2718「NOI2018」归程

    [题解] 本题有多种做法,例如可持久化并查集.kruskal重构树等. kruskal重构树的做法是这样的:先把边按照海拔h从大到小的顺序排序,然后跑kruskal建立海拔的最大生成树,顺便建krus ...

  5. LOJ #2718. 「NOI2018」归程 Dijkstra+可持久化并查集

    把 $Noi2018$ day1t1 想出来还是挺开心的,虽然是一道水题~ 预处理出来 1 号点到其它点的最短路,然后预处理边权从大到小排序后加入前 $i$ 个边的并查集. 这个并查集用可持久化线段树 ...

  6. 「NOI2018」归程

    「NOI2018」归程 题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 >\(1\) 个节点. \(m\) 条边的无向连通图(节点的编号从 \( ...

  7. NOI Day1T1归程(Kruskal重构树+Dijkstra)

    NOI Day1T1归程(Kruskal重构树+Dijkstra) 题目 洛谷题目传送门 题解 其实我不想写......,所以...... 挖个坑......我以后一定会补的 luogu的题解讲的还是 ...

  8. LOJ.2718.[NOI2018]归程(Kruskal重构树 倍增)

    LOJ2718 BZOJ5415 洛谷P4768 Rank3+Rank1无压力 BZOJ最初还不是一道权限题... Update 2019.1.5 UOJ上被hack了....好像是纯一条链的数据过不 ...

  9. [NOI2018]归程 kruskal重构树

    [NOI2018]归程 LG传送门 kruskal重构树模板题. 另一篇文章里有关于kruskal重构树更详细的介绍和更板子的题目. 题意懒得说了,这题的关键在于快速找出从查询的点出发能到达的点(即经 ...

随机推荐

  1. Oracle / PLSQL函数 - DECODE

    1.DECODE( expression , search , result [, search , result]... [, default] ) 参数说明: expression : 表中的某一 ...

  2. Day6 模块及Python常用模块

    模块概述 定义:模块,用一砣代码实现了某类功能的代码集合. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,提供了代码的重用性.在Python中,一个.py文件就称之为一个模块(Mod ...

  3. jQuery事件--keypress([[data],fn])和trigger(type,[data])

    keypress([[data],fn]) 概述 当键盘或按钮被按下时,发生 keypress 事件 keypress 事件与 keydown 事件类似.当按钮被按下时,会发生该事件.它发生在当前获得 ...

  4. 【2017-04-17】类库、通用变量、is和as、委托

    类库dll文件,里边有很多被编译后的C#代码,不可阅读,不可修改,只能调用 1.类库创建 新建项目为类库,类库文件编写完成后,选择生成—生成解决方案,在debug文件夹下找到dll文件 2.类库引用 ...

  5. xshell的一些基本操作

    挺全面的一篇文章,没事可以看看. (1)命令ls——列出文件  ls -la 给出当前目录下所有文件的一个长列表,包括以句点开头的“隐藏”文件  ls a* 列出当前目录下以字母a开头的所有文件  l ...

  6. 前端规范--eslint standard

    https://github.com/standard/standard/blob/master/docs/RULES-zhcn.md

  7. 什么是FEBS

    FEBS后台权限管理系统      FEBS是一个简单高效的后台权限管理系统.项目基础框架采用全新的Java Web开发框架 —— Spring Boot2.0.4,消除了繁杂的XML配置,使得二次开 ...

  8. CRM 权限设置 ss

    表结构的设计 权限表 url -url地址的正则表达式 ^$ title - 标题 角色表 name - 角色名称 permissions 多对多关联权限表 (权限和角色的关系表) 用户表 name ...

  9. 记账本微信小程序开发三

    一.制作登陆界面: 更改全局配置,改颜色,名称: 界面 格式 登录界面 二.页面的跳转 按钮的设置 注册事件 结果

  10. Linux 执行文本保存报错 是使用了记事本等工具打开之后导致的

    dos2unix xx.bat    格式化下就好了 安装: yum install -y dos2unix