[NOI2018 歸程] 可持久化並查集 Dijkstra
想看克魯斯卡爾重構樹戳我
又學了一個解法暴力艹過了此題
所以常數還是一個很重要的東西啦2333
對於這個題可以使用可持久化並查集維護每個聯通塊中離一號點最近的距離
只要對於加邊的海拔倒序可持久化就可以了 具體實現 見程式碼應該很好懂
時間複雜度
有一個東西自己想了會
就是為什麼更新聯通塊深度的時候不用新開點但是更新聯通塊答案的時候要
首先對於當前這個版本更新深度的時候 只會對當前這個版本造成影響 那麼就不用新開點
但是更新聯通塊答案的時候 指標可能會指回之前的版本 如果直接修改的話就影響了之前的版本
所以必須要新開節點來記錄這個變化
update:更新深度的時候已經新開過一個版本於是就不用加點了ovo
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#define For(i, a, b) for(register int i = a; i <= b; ++ i)
#define FOR(i, a, b) for(register int i = a; i >= b; -- i)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])
#define PLI pair<ll, int>
#define mp make_pair
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10, maxm = 4e5 + 10;
int to[maxm << 1], head[maxn], nxt[maxm << 1], a[maxm << 1], l[maxm << 1], e;
int n, m, T, tmp[maxm], root[maxm], num, now, Q, K, S;
ll dis[maxn], lastans;
struct edge
{
int x, y, L, A;
}E[maxn << 2];
template<class T>inline bool chkmin(T &_, T __)
{
return _ > __ ? _ = __, 1 : 0;
}
bool cmp(edge X, edge Y)
{
return X.A < Y.A;
}
inline int read()
{
int _ = 0, ___ = 1, __ = getchar();
for(; !isdigit(__); __ = getchar()) if(__ == '-') ___ = -1;
for(; isdigit(__); __ = getchar()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
return _ * ___;
}
void add(int x, int y, int L, int A)
{
to[++ e] = y;
nxt[e] = head[x];
head[x] = e;
l[e] = L;
a[e] = A;
}
void Dijkstra()
{
priority_queue<PLI, vector<PLI>, greater<PLI> >q;
For(i, 1, n)
dis[i] = 1e18;
q.push(mp(dis[1] = 0, 1));
while(!q.empty())
{
PLI k = q.top(); q.pop();
if(k.first > dis[k.second])
continue;
go(k.second, i)
if(chkmin(dis[to[i]], dis[k.second] + l[i]))
q.push(mp(dis[to[i]], to[i]));
}
}
namespace Chairman_Tree
{
#define ls(x) (T[x].ch[0])
#define rs(x) (T[x].ch[1])
#define mid ((l + r) >> 1)
int cnt;
struct node
{
int ch[2], fa, height;
ll ans;
}T[maxm * 40];
void build(int &x, int l, int r)
{
x = ++ cnt;
if(l == r)
T[x].fa = l, T[x].ans = dis[l];
else
{
build(ls(x), l, mid);
build(rs(x), mid + 1, r);
}
}
int query(int x, int l, int r, int p)
{
if(l == r)
return x;
if(p <= mid)
return query(ls(x), l, mid, p);
return query(rs(x), mid + 1, r, p);
}
void update(int &x, int pre, int l, int r, int p, int dad)
{
T[x = ++ cnt] = T[pre];
if(l == r)
T[x].fa = dad;
else
{
if(p <= mid)
update(ls(x), ls(pre), l, mid, p, dad);
else
update(rs(x), rs(pre), mid + 1, r, p, dad);
}
}
void updateans(int &x, int pre, int l, int r, int p, ll res)
{
T[x = ++ cnt] = T[pre];
if(l == r)
T[x].ans = res;
else
{
if(p <= mid)
updateans(ls(x), ls(pre), l, mid, p, res);
else
updateans(rs(x), rs(pre), mid + 1, r, p, res);
}
}
void add(int x, int l, int r, int p)
{
if(l == r)
++ T[x].height;
else
{
if(p <= mid)
add(ls(x), l, mid, p);
else
add(rs(x), mid + 1, r, p);
}
}
int find(int rt, int x)
{
int now = query(rt, 1, n, x);
while(T[now].fa != x)
{
x = T[now].fa;
now = query(rt, 1, n, x);
}
return now;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("4768.in", "r", stdin);
freopen("4768.out", "w", stdout);
#endif
int x, y;
for(T = read(); T -- ; )
{
For(i, 1, n) head[i] = 0;
n = read(), now = m = read();
Chairman_Tree::cnt = lastans = e = 0;
For(i, 1, m)
{
E[i] = (edge){read(), read(), read(), read()};
add(E[i].x, E[i].y, E[i].L, E[i].A);
add(E[i].y, E[i].x, E[i].L, E[i].A);
tmp[i] = E[i].A;
}
Dijkstra();
sort(tmp + 1, tmp + m + 1);
sort(E + 1, E + m + 1, cmp);
num = unique(tmp + 1, tmp + m + 1) - tmp - 1;
Chairman_Tree::build(root[num + 1], 1, n);
FOR(i, num, 1)
{
root[i] = root[i + 1];
while(tmp[i] == E[now].A)
{
int u = Chairman_Tree::find(root[i], E[now].x), v = Chairman_Tree::find(root[i], E[now].y);
if(Chairman_Tree::T[u].fa != Chairman_Tree::T[v].fa)
{
if(Chairman_Tree::T[u].height > Chairman_Tree::T[v].height)
swap(u, v);
Chairman_Tree::update(root[i], root[i], 1, n, Chairman_Tree::T[u].fa, Chairman_Tree::T[v].fa);
if(Chairman_Tree::T[u].ans < Chairman_Tree::T[v].ans)
Chairman_Tree::updateans(root[i], root[i], 1, n, Chairman_Tree::T[v].fa, Chairman_Tree::T[u].ans);
if(Chairman_Tree::T[u].height == Chairman_Tree::T[v].height)
Chairman_Tree::add(root[i], 1, n, Chairman_Tree::T[v].fa);
}
-- now;
}
}
Q = read(), K = read(), S = read();
while(Q --)
{
x = read(), y = read();
x = (lastans * K + x - 1) % n + 1;
y = (lastans * K + y) % (S + 1);
y = upper_bound(tmp + 1, tmp + num + 1, y) - tmp;
printf("%lld\n", lastans = Chairman_Tree::T[Chairman_Tree::find(root[y], x)].ans);
}
}
return 0;
}
相關推薦
[NOI2018 歸程] 可持久化並查集 Dijkstra
想看克魯斯卡爾重構樹戳我 又學了一個解法暴力艹過了此題 所以常數還是一個很重要的東西啦2333 對於這個題可以使用可持久化並查集維護每個聯通塊中離一號點最近的距離 只要對於加邊的海拔倒序可持久化就可以了 具體實現 見程式碼應該很好懂 時間複雜度 O(n
BZOJ5415:[NOI2018]歸程(可持久化並查集,最短路)
Description Input Output Sample Input1 1 4 3 1 2 50 1 2 3 100 2 3 4 50 1 5 0 2 3 0 2 1 4 1 3 1 3 2 Sample Output1 0 50 200 50
UOJ 393 【NOI2018】歸程——可持久化並查集
sed while urn ocr noi pan algo node -- 題目:http://uoj.ac/problem/393 題解:https://www.cnblogs.com/HocRiser/p/9368067.html 但過不了 UOJ 的 hack 數據
可持久化並查集加強版 BZOJ 3674
log 歷史 clear 必須 new 路徑壓縮 都是 return 父節點 http://www.lydsy.com/JudgeOnline/problem.php?id=3674 3674: 可持久化並查集加強版 Time Limit: 15 Sec Memory
BZOJ 3674 可持久化並查集加強版(主席樹變形)
als ret desc scan sync scanf ops 只需要 ica 3673: 可持久化並查集 by zky Time Limit: 5 Sec Memory Limit: 128 MB Submit: 2515 Solved: 1107 [
[BZOJ 3551] Peaks 半可持久化並查集 可持久化線段樹合並
algorithm roo i++ name def amp merge zoj 可持久化線段樹 實現 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib>
bzoj 3674: 可持久化並查集加強版
合並 struct 操作 logs void des 查詢 uil 出了 Description Description: 自從zkysb出了可持久化並查集後…… hzwer:亂寫能AC,暴力踩標程 KuribohG:我不路徑壓縮就過了! ndsf:暴力就可以輕松虐! z
可持久化並查集
比較 root urn amp down truct 表示 void roo 如果不采用路徑壓縮而只采用按秩合並,那麽並查集的可持久化是比較容易實現的。按秩合並可以保證一棵 $n$ 個節點的樹的高度是 $O(\log n)$ 的。 實現方法: 用 $r_v$ 表示 $v$
洛谷P3402 【模板】可持久化並查集(可持久化線段樹,線段樹)
std 樹節點 https case 深度 build eof spa 復雜度 orz TPLY 巨佬,題解講的挺好的。 這裏重點梳理一下思路,做一個小小的補充吧。 寫可持久化線段樹,葉子節點維護每個位置的fa,利用每次只更新一個節點的特性,每次插入\(logN\)個節點,
[bzoj] 3673 3674 可持久化並查集 || 可持久化數組
www. getchar 可持久化線段樹 for puts == markdown efi query 原題 加強版 題意: 可持久化並查集模板…… 題解: 用可持久化線段樹維護一個可持久化數組,來記錄每一次操作後的狀態。 不能用路徑壓縮,但是要按置合並,使復雜度保證在O(
可持久化並查集總結
模板題 線段樹 它的 blog 節點 ldb efi 主席樹 tdi 可持久化並查集總結
可持久化並查集模板
#include<bits/stdc++.h> using namespace std; const int MAXN=1e6+2333; int n,m; int a[MAXN]; struct Persistable_Segment_Tree{ struc
bzoj 3673 可持久化並查集 by zky
Description n個集合 m個操作操作:1 a b 合併a,b所在集合2 k 回到第k次操作之後的狀態(查詢算作操作)3 a b 詢問a,b是否屬於同一集合,是則輸出1否則輸出0 0<n,m<=2*10^4 Input Outpu
P3402 【模板】可持久化並查集
res void include getc pro case bits const data 傳送門 //minamoto #include<bits/stdc++.h> using namespace std; #define getc() (p1==p2&a
BZOJ3674:可持久化並查集加強版
淺談主席樹:https://www.cnblogs.com/AKMer/p/9956734.html 題目傳送門:https://www.lydsy.com/JudgeOnline/problem.php?id=3674 因為要支援歷史操作,所以我們用可持久化線段樹來維護並查集的祖先陣列。 因為要路徑壓
解題:洛谷3402 可持久化並查集
題面 滾下去補學考之前更一發 基於可持久化線段樹實現(我不很喜歡“主席樹”這個名字),注意不能路徑壓縮,首先這與可持久化的思想衝突(即儘量利用之前已有的部分,只修改有修改的部分,路徑壓縮壓完了豈不是gg),其次每次壓縮還會有一個log的時間複雜度(查詢),然後複雜度就是$O(n\log n)$的
[學習筆記]可持久化資料結構 可持久化並查集
可持久化:支援查詢歷史版本和在歷史版本上修改 可持久化陣列 主席樹做即可。 【模板】可持久化陣列(可持久化線段樹/平衡樹) 可持久化並查集 可持久化並查集 主席樹做即可。 要按秩合併。(路徑壓縮每次建logn條鏈,會卡爆空間MLE) 主席樹節點,維護father
BZOJ 3674: 可持久化並查集模板
Code: #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std; void setIO(string a){ fr
可持久化專題(三)——可持久化並查集
前言 要學可持久化並查集,必須先會可持久化陣列。 簡介 可持久化並查集應該是一個挺實用的資料結構(例如NOI2018Day1T1中就有它的身影)。 它主要建立於可持久化陣列的基礎之上(而可持久化陣列
關於可持久化並查集的學習和思考
鑑於noip比賽前集訓時SAKER前輩教了我這個蒟蒻可持久化線段樹以來,我懂得了如何維護一個支援歷史查詢的線段樹。於是我就開始異想天開了:可不可以快速維護一個支援歷史查詢的陣列呢? 就在這時,我上網看到了一個新的演算法:可持久化並查集。先用例題來講吧: BZOJ3674:可