并不会写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学习笔记21:MVC案例完整实践(part 2)---DAO层设计

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

  2. 【DP】最长不下降子序列问题(二分)

    Description 给你一个长度为n的整数序列,按从左往右的顺序选择尽量多的数字并且满足这些数字不下降. Thinking 朴素dp算法:F[i]表示到第i位为止的最长不下降子序列长度 F[i]= ...

  3. 【转】RMQ-ST算法详解

    地址:http://blog.csdn.net/z287438743z/article/details/8132806 RMQ(Range Minimum/Maximum Query)问题就是求区间最 ...

  4. 算法(Algorithms)第4版 练习 1.5.2

    0 1 2 3 4 5 6 7 8 9 10 components 9 0 0 1 2 3 4 5 6 7 8 9 components 3 4 0 1 2 4 5 6 7 8 0 8 compone ...

  5. centos5.5源更新方法(网易163源 很快的源)

    http://blog.chinaunix.net/uid-24993824-id-189965.html 1.首先,打开终端,获取root权限 2.关闭fastestmirror插件         ...

  6. node Express安装和使用

    1:在cmd命令行下执行npm install -g express,安装全局的express 2:进入需要创建项目的目录下执行express nodeExpressProject,创建express ...

  7. java_面试_01_一个月的面试总结(java)

    重点知识 由于我面试的JAVA开发工程师,针对于JAVA,需要理解的重点内容有: JVM内存管理机制和垃圾回收机制(基本每次面试都会问,一定要搞得透彻) JVM内存调优(了解是怎么回事,一般做项目过程 ...

  8. php判断是否是微信浏览器

    php判断是否是微信浏览器 直接上代码: <?PHP function is_wechat_browser(){ $user_agent = $_SERVER['HTTP_USER_AGENT' ...

  9. 关键字volidate和transient(转)

    Volatile修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值.而且,当成员变量发生变化时,强迫线程将变化值回写到主内存.这样在任何时刻,两个不同的线程总是看到某个成员变量的同一 ...

  10. 理解SetCapture()和ReleaseCapture()及GetCapture()作用

    正常情况下,鼠标指针位于哪个窗口区域内,鼠标消息就自动发给哪个窗口.如果调用了SetCapture,之后无论鼠标的位置在哪,鼠标消息都发给指定的这个窗口,直到调用ReleaseCapture或者调用S ...