并不会写Kruskal重构树,两个$log$跑得比较卡。

首先考虑一下没有强制在线的要求怎么办,有一个比较容易想到的做法就是先跑一遍最短路,然后把所有边按照海拔从大到小排序,把所有询问的海拔也从大到小排序,然后对于每一个询问$(x, h)$把所有海拔高于$h$的边都连上,然后看一看点$x$的能到达的点中离$1$距离最小的点是多少。

这就是一个并查集可以维护的东西了。

强制在线怎么办……直接大力把这个并查集可持久化……就没了。

时间复杂度$O(nlog^2n)$。

Code:

#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair <int, int> pin; const int N = 2e5 + ;
const int M = 5e5 + ; int testCase, n, m, qn, tot, head[N], dis[N];
int numCnt, in[M], ed[M], pos[M];
bool vis[N]; struct Edge {
int to, nxt, val;
} e[M << ]; inline void add(int from, int to, int val) {
e[++tot].to = to;
e[tot].val = val;
e[tot].nxt = head[from];
head[from] = tot;
} struct Pathway {
int x, y, val, h, id;
} path[M]; bool cmp(const Pathway &u, const Pathway &v) {
return u.h < v.h;
} inline void read(int &X) {
X = ; char ch = ; int op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} priority_queue <pin> Q;
void dij() {
memset(dis, 0x3f, sizeof(dis));
memset(vis, , sizeof(vis));
Q.push(pin(dis[] = , ));
for(; !Q.empty(); ) {
int x = Q.top().second; Q.pop();
if(vis[x]) continue;
vis[x] = ;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(dis[y] > dis[x] + e[i].val) {
dis[y] = dis[x] + e[i].val;
Q.push(pin(-dis[y], y));
}
}
}
} inline int min(int x, int y) {
return x > y ? y : x;
} inline void swap(int &x, int &y) {
int t = x; x = y; y = t;
} namespace PUfs {
struct Node {
int lc, rc, ufs, dis, dep;
} s[N * ];
int root[M], nodeCnt = 0LL; #define lc(p) s[p].lc
#define rc(p) s[p].rc
#define ufs(p) s[p].ufs
#define dis(p) s[p].dis
#define dep(p) s[p].dep
#define mid ((l + r) >> 1) inline void clear(int p) {
lc(p) = rc(p) = ufs(p) = dis(p) = dep(p) = ;
} void build(int &p, int l, int r, int type) {
p = ++nodeCnt;
if(l == r) {
ufs(p) = l, dep(p) = ;
if(!type) dis(p) = ;
else dis(p) = dis[l];
return;
} build(lc(p), l, mid, type);
build(rc(p), mid + , r, type);
} void modify(int &p, int l, int r, int x, int nowUfs, int nowDis, int pre) {
p = ++nodeCnt;
if(l == r) {
ufs(p) = nowUfs, dis(p) = nowDis, dep(p) = dep(pre);
return;
} lc(p) = lc(pre), rc(p) = rc(pre);
if(x <= mid) modify(lc(p), l, mid, x, nowUfs, nowDis, lc(pre));
else modify(rc(p), mid + , r, x, nowUfs, nowDis, rc(pre));
} void mDis(int &p, int l, int r, int x, int nowDis, int pre) {
p = ++nodeCnt;
if(l == r) {
ufs(p) = ufs(pre), dep(p) = dep(pre), dis(p) = nowDis;
return;
} lc(p) = lc(pre), rc(p) = rc(pre);
if(x <= mid) mDis(lc(p), l, mid, x, nowDis, lc(pre));
else mDis(rc(p), mid + , r, x, nowDis, rc(pre));
} void add(int p, int l, int r, int x) {
if(l == r) {
++dep(p);
return;
} if(x <= mid) add(lc(p), l, mid, x);
else add(rc(p), mid + , r, x);
} int query(int p, int l, int r, int x) {
if(l == x && x == r) return p; if(x <= mid) return query(lc(p), l, mid, x);
else return query(rc(p), mid + , r, x);
} int find(int now, int x) {
int f = query(now, , n, x);
if(s[f].ufs == x) return f;
else return find(now, s[f].ufs);
} inline void merge(int k, int x, int y) {
root[k] = root[k + ];
int fx = find(root[k], x), fy = find(root[k], y);
if(s[fx].ufs == s[fy].ufs) return;
if(s[fx].dep > s[fy].dep) swap(fx, fy);
int dis = min(s[fx].dis, s[fy].dis);
modify(root[k], , n, s[fx].ufs, s[fy].ufs, dis, root[k + ]);
if(dis != s[fy].dis) mDis(root[k], , n, s[fy].ufs, dis, root[k]);
if(s[fx].dep == s[fy].dep) add(root[k], , n, s[fy].ufs);
} #undef mid } using namespace PUfs; inline void prework() {
for(int i = ; i <= nodeCnt; i++) clear(i);
nodeCnt = ; memset(root, , sizeof(root));
build(root[m + ], , n, );
build(root[], , n, );
} inline int bfind(int val) {
int ln = , rn = numCnt, mid, res;
for(; ln <= rn; ) {
mid = (ln + rn) / ;
if(in[mid] <= val) ln = mid + , res = mid;
else rn = mid - ;
}
return res;
} int main() {
// freopen("return.in", "r", stdin);
// freopen("return.out", "w", stdout); for(read(testCase); testCase--; ) {
tot = numCnt = ; memset(head, , sizeof(head)); read(n), read(m);
for(int i = ; i <= m; i++) {
read(path[i].x), read(path[i].y), read(path[i].val), read(path[i].h);
in[++numCnt] = path[i].h;
path[i].id = i;
add(path[i].x, path[i].y, path[i].val), add(path[i].y, path[i].x, path[i].val);
} sort(in + , in + + numCnt);
numCnt = unique(in + , in + + numCnt) - in - ; /* for(int i = 1; i <= numCnt; i++)
printf("%d ", in[i]);
printf("\n"); */ for(int i = ; i <= m; i++)
path[i].h = lower_bound(in + , in + + numCnt, path[i].h) - in; dij(); /* for(int i = 1; i <= n; i++)
printf("%d ", dis[i]);
printf("\n"); */ prework();
sort(path + , path + + m, cmp); /* printf("\n");
for(int i = 1; i <= m; i++)
printf("%d %d %d %d\n", path[i].x, path[i].y, path[i].val, path[i].h); */ for(int i = m; i >= ; i--) {
merge(i, path[i].x, path[i].y);
if(path[i].h != path[i - ].h) ed[path[i].h] = i;
} /* for(int i = 1; i <= numCnt; i++)
printf("%d ", ed[i]);
printf("\n"); */ for(int i = ; i < numCnt; i++) pos[i] = ed[i + ];
pos[numCnt] = m + ; /* for(int i = 1; i <= numCnt; i++)
printf("%d ", pos[i]);
printf("\n"); */ /* for(int j = 1; j <= n; j++)
printf("%d ", s[query(root[m + 1], 1, n, j)].dis);
printf("\n"); */ /* for(int i = 1; i <= numCnt; i++) {
printf("%d: ", i);
for(int j = 1; j <= n; j++)
printf("%d ", s[query(root[pos[i]], 1, n, j)].ufs);
printf("\n");
} for(int i = 1; i <= numCnt; i++) {
printf("%d: ", i);
for(int j = 1; j <= n; j++)
printf("%d ", s[query(root[pos[i]], 1, n, j)].dis);
printf("\n");
} */ /* for(int i = 1; i <= numCnt; i++)
printf("%d ", pos[i]);
printf("\n"); */ // in[++numCnt] = path[m].h + 1; int K, S, ans = ;
read(qn), read(K), read(S);
for(int x, h; qn--; ) {
read(x), read(h);
x = (x + K * ans - ) % n + , h = (h + K * ans) % (S + );
int rt = bfind(h);
rt = pos[rt];
rt = find(root[rt], x);
printf("%d\n", ans = s[rt].dis);
}
}
return ;
}

缅怀$spfa$。

Luogu 4768 [NOI2018]归程的更多相关文章

  1. Luogu P4768 [NOI2018]归程(Dijkstra+Kruskal重构树)

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

  2. Luogu P4768 [NOI2018]归程

    题目链接 \(Click\) \(Here\) \(Kruskal\)重构树的好题.想到的话就很好写,想不到乱搞的难度反而相当高. 按照点的水位,建出来满足小根队性质的\(Kruskal\)重构树,这 ...

  3. [luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树)

    [luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树) 题面 题面较长,这里就不贴了 分析 看到不能经过有积水的边,即不能经过边权小于一定值的边,我们想到了kru ...

  4. [NOI2018]归程 kruskal重构树

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

  5. [洛谷P4768] [NOI2018]归程 (kruskal重构树模板讲解)

    洛谷题目链接:[NOI2018]归程 因为题面复制过来有点炸格式,所以要看题目就点一下链接吧\(qwq\) 题意: 在一张无向图上,每一条边都有一个长度和海拔高度,小\(Y\)的家在\(1\)节点,并 ...

  6. NOI2018 D1T1 [NOI2018]归程 解题报告

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

  7. BZOJ_5415_[Noi2018]归程_kruscal重构树+倍增+最短路

    BZOJ_5415_[Noi2018]归程_kruscal重构树+倍增 Description www.lydsy.com/JudgeOnline/upload/noi2018day1.pdf 好久不 ...

  8. 题解 NOI2018 归程

    题解 NOI2018 归程 题意 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 n 个节点.m 条边的无向连通图(节点的编号从 1 至 n).我们依次用 l, ...

  9. [NOI2018]归程(kruscal重构树)

    [NOI2018]归程 题面太长辣,戳这里 模拟赛上写了一个spfa (关于spfa,它已经死了),然后一个st表水完暴力跑路.考后说是Kruscal重构树或者可持久化并查集???这都是些什么东西.不 ...

随机推荐

  1. [原创]java WEB学习笔记31:会话与状态管理 session机制 概述(定义,session机制,session的声明周期,保存session的方式,Session的创建与删除)

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  2. 第二篇、HTML

    一.html文档树 二.HTML分类 块级标签和内联标签: 块级标签:<p><h1><table><ol><ul><form>& ...

  3. 英语发音规则---Z字母

    英语发音规则---Z字母 一.总结 一句话总结:字母Z的名称zed /zed/,美式英语的称zee /zi:/,少数方言(如香港)读izzard /'izəɹd/. 1.字母Z在单词中发[z]? pu ...

  4. define的用法与注意事项

    ------------------------------------------------- 在编程使用宏替换时,当字符串中不只一个符号时,加上括号表现出优先级, 如果是带参数的宏定义,则要给宏 ...

  5. UI层自动化测试介绍

    UI指的是用户可以用肉眼可以看到的页面. UI层自动化测试的原理.不论是web端还是移动端,原理都是一样的,就是基于页面元素的识别和定位来进行模拟用户行为. 首先识别到某个元素,比如一个按钮,然后定义 ...

  6. 数据库ACID和mvcc

    一.数据库的ACID性: 原子性(atomicity).一致性(consistency).隔离性(isolation).持久性(durability). 二.原子性 1.原子性:一个事务要么全部完成, ...

  7. ffmpeg命令选项解释

    ffmpeg作为媒体文件处理软件,基本用法如下: ffmpeg -i INPUTfile [OPTIONS] OUTPUTfile 输入输出文件通常就是待处理的多媒体文件了.可以是纯粹的音频文件,纯粹 ...

  8. 【JVM】java棧

    java棧和函数调用的关系图 [名词解释]--->java棧是一块线程的私有空间--->java的棧是先进后出的数据结构.函数返回,则该函数的棧帧被弹出.--->一个函数对应一个棧帧 ...

  9. 51nod 1149 Pi的递推式 组合数

    题目大意: \(F(x) = 1 (0 \leq x < 4)\) \(F(x) = F(x-1) + F(x-\pi) (4 \leq x)\) 给定\(n\),求\(F(n)\) 题解: 我 ...

  10. bzoj 4034: 树上操作 线段树

    题目: 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 ...