并不会写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. [转]eclipse中的常用快捷键

    1.选中你要加注释的区域,用ctrl+shift+C 会加上//注释2.先把你要注释的东西选中,用shit+ctrl+/ 会加上注释3.要修改在eclispe中的命令的快捷键方式我们只需进入windo ...

  2. 【leetcode刷题笔记】Implement strStr()

    Implement strStr(). Returns a pointer to the first occurrence of needle in haystack, or null if need ...

  3. Cache-Control常用类型

    可缓存性 no-cache 强制所有缓存了此响应的缓存用户,在使用已存储的缓存数据前,发送条件请求到原始服务器,若未过期,则使用缓存数据,否则重新获取 no-store 不存储有关客户端请求或服务器响 ...

  4. 第七篇、os、sys、random、time、datetime、logging

    一.sys 用于提供对Python解释器相关的操作: 1 2 3 4 5 6 7 8 9 sys.argv           命令行参数List,第一个元素是程序本身路径 sys.exit(n)   ...

  5. JavaScript的undefined与null、NaN的区别

    Javascript的数据类型 在JavaScript中,有三种住数据类型.两种复合数据类型和两种特殊数据类型. 1.主数据类型(基元数据类型) 字符串 String数据类型: 字符串值是一个由零个或 ...

  6. HDU 2736 Surprising Strings

                                    Surprising Strings Time Limit:1000MS     Memory Limit:65536KB     64 ...

  7. 在webBrowser中取Cookie的方法

    在很多情况下我们会使用间进程的webBrowser去实现一些网页的请求和抓去,这个时候有部分网页是取不到Cookie的,那怎么办呢?下面我提供一个方法,应该99%的都能取到, //取当前webBrow ...

  8. Hadoop 2.x简介

    Hadoop 2.0产生背景 Hadoop1.0中HDFS和MapReduce在高可用.扩展性等方面存在问题 HDFS存在的问题 NameNode单点故障,难以应用于在线场景 NameNode压力过大 ...

  9. js文件报错Syntax error on token "Invalid Regular Expression Options", no accurate correction

    Syntax error on token "Invalid Regular Expression Options", no accurate correction 1.选中报错的 ...

  10. 快速构建hibernate框架

    手动配置Hibernate框架的配置,极易出现问题,在Eclipse的web项目中,我们可以快速配置,方便快捷 一.导入Hibernate框架所需要的jar文件 二. 窗口—Perspective—打 ...