链接

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. c# 使用MS SqlServer,连接成功,但是还报异常A connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0。。。。

    c# 使用MS SqlServer,连接成功,但是还报异常A connection was successfully established with the server, but then an ...

  2. 并发工具CyclicBarrier

    想想一下这样一个场景,有多个人需要过河,河上有一条船,船要等待满10个人才过河,过完河后每个人又各自行动. 这里的人相当于线程,注意这里,每个线程运行到一半的时候,它就要等待一个条件,即船满过河的条件 ...

  3. python读取大文件

    最近在学习python的过程中接触到了python对文件的读取.python读取文件一般情况是利用open()函数以及read()函数来完成: f = open(filename,'r') f.rea ...

  4. json为txt文本加密

    我们知道json是一种数据传输的加密格式 这里为txt格式的文本加密(纯属无聊)   写的比较凌乱,查找你输入的两个文件夹下面的所有txt文件(包含下一级文件): 运行时要注意,别把重要文件给加密了 ...

  5. Linux 查看磁盘或文件夹及文件大小

    当磁盘大小超过标准时会有报警提示,这时如果掌握df和du命令是非常明智的选择. df可以查看一级文件夹大小.使用比例.档案系统及其挂入点,但对文件却无能为力.    du可以查看文件及文件夹的大小. ...

  6. npm 包下载的各种姿势

    最近在写Node程序的时候,突然对 npm install 的-save和-save-dev 这两个参数的使用比较混乱.其实博主在这之前对这两个参数的理解也是模糊的,各种查资料和实践后对它们之间的异同 ...

  7. 转:【专题三】自定义Web服务器

    前言: 经过前面的专题中对网络层协议和HTTP协议的简单介绍相信大家对网络中的协议有了大致的了解的, 本专题将针对HTTP协议定义一个Web服务器,我们平常浏览网页通过在浏览器中输入一个网址就可以看到 ...

  8. myeclipse项目导入IDEA

    1.import project from external model     (不需要删除myeclipse多余文件) https://blog.csdn.net/d276031034/artic ...

  9. JS报错修改日记(1):Uncaught ReferenceError: showQRcode is not defined

    为了加一个查看二维码的功能,如: //页面内按钮 <a class="manipulate-btn" href="#" onclick="sho ...

  10. [转载]表单校验之datatype

    凡要验证格式的元素均需绑定datatype属性,datatype可选值内置有10类,用来指定不同的验证格式. 如果还不能满足您的验证需求,可以传入自定义datatype,自定义datatype是一个非 ...