链接

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. 记无法用被动方式登录远程linux主机的原因

    [环境]: linux主机:华为企业云 ftp服务端:vsftpd 客户端:ftp命令行工具,安卓端ES文件浏览器 [现象]: 在ES文件浏览器中,使用被动方式没法连接,使用主动方式可以连接,但是没法 ...

  2. 去掉idea中竖线

    1.现象如下: 2.解决办法. 3.解决后如下:

  3. js切换背景颜色

    我将全部的代码上传到了github,你可以下载查看 <!-------change the background color--------------> <script> f ...

  4. Sql server 存储过程批量插入若干数据。

    测试时,经常需要生成大量数据来测试系统性能,此功能可以用存储过程快速生成. 1. 随机生成日期 DECLARE @Date_start datetime DECLARE @Date_end datet ...

  5. arc 092D Two Sequences

    题意: 给出两个长度N相同的整数序列A和B,有N^2种方式从A中选择一个数Ai,从B中选择一个数Bj,让两个数相加,求这N^2个数的XOR,即异或. 思路: 暴力的求显然是会超时的,因为是异或,就考虑 ...

  6. 即时通讯(I)

    网络通讯三要素: 网络七层协议划分: 网络五层协议的划分: 要记网络层的5层协议,可以把它想像为一枚洋葱.学过计算机网络的,看到这个网络协议的套接字,大概就会明白了!它是一层一层的进行包裹的,然后交由 ...

  7. 凯撒密码、GDP格式化输出、99乘法表

    1.恺撒密码的编码 s=input('明文:') print('密文:',end='') for i in s: print(chr(ord(i)+3),end='') 附加: print('字符串的 ...

  8. Qt Md5应用示例

    [1].cpp文件 #include "widget.h" #include "ui_widget.h" #include <QCryptographic ...

  9. CachedIntrospectionResults 初始化

  10. JS实战

    1. jquery取消点击事件 $("#dashboard").unbind("click"); 2.jquery绑定鼠标滑过,离开事件 $("#da ...