NOI2018归程

Problem

传送门

给一张图,每条边有两个参数:$w,h$,分别代表边长与海拔

有Q个询问,每次询问从给定点出发,借助一辆只能走海拔大于h的边的车后,到一号点最短步行的距离。

Solution

正解是$kruskal$重构树,很好理解,也很好打,网上题解很多,这里就不讲了

下面我们讲一下一个需吸氧且随缘$T$点的做法

可持久化并查集

有没有感觉十分高端大气上档次?

表示没有

之前上网看过这中算法,但是一直没有看懂

右边大佬这个是怎么实现的

结果

右边大佬:啊?……不就是可持久化那样搞一下……按质合并……就可以了吗?

本$caiji$表示听不懂……

后来跑到隔壁的时候$Na_2S_2O_3\ zbr$提了一嘴

$Na_2S_2O_3$:你别$fake$啰

$caiji$:我是真不会

$zbr$:啊?就是拿个可持久化数组维护一下并查集就可以了啊

$caiji$:可持久化数组是什么?

$Na_2S_2O_3$:就是用主席树维护一个数组啊……

(恍然大雾.jpg)

咳咳,跑题了。

现在来讲一讲这道题

先考虑离线,就是先跑一边最短路

询问与边都按从大到小排序,然后每次询问倒序进去的时候按比海拔高的约束加入能开车的边,

这个东西可以用并查集维护。

每次维护这个联通块中到一号点的最小距离,直接输出就好了。

接下来就是在线了

某位伟大的博士曾说过

勇矢博士:所有可离线做的问题都可以用可持久化数据结构在线做。

好像很有道理……

所以我们这道题可以用可持久化并查集……

对于每个询问,我们只需回到没有添加海拔比$q$小的路的版本,再查询$v$所在的联通块即可

复杂度$O(n(logn)^2)$

卡卡常,好像能过个鬼

拷了个海波的快输还是T了两点

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#include<bits/stdc++.h>
#define mp make_pair
#define fst first
#define snd second
#define LL long long
#define pli pair<LL,int>
#define pii pair<int,int>

using namespace std;

inline int read(){
int res = 0, fl = 1;
char r = getchar();
for (; !isdigit(r); r = getchar()) if(r == '-') fl = -1;
for (; isdigit(r); r = getchar()) res = (res << 3) + (res << 1) + r - 48;
return res * fl;
}

char _obuf[1 << 20], _stk[20];
class Ostream
{
char *opos, *oend, *stkpos;

public :
Ostream()
{
oend = (opos = _obuf) + (1 << 20);
stkpos = _stk;
}

~Ostream()
{ fwrite(_obuf, 1, opos - _obuf, stdout); }

void Putchar(char ch)
{
*opos++ = ch;
if(opos == oend)
{
fwrite(_obuf, 1, 1 << 20, stdout);
opos = _obuf;
}
}

Ostream& operator<<(int n)
{
do
{
*++stkpos = n % 10 ^ 48;
n /= 10;
} while(n);
while(stkpos != _stk)
Putchar(*stkpos--);
return *this;
}

Ostream& operator<<(char c)
{
Putchar(c);
return *this;
}

Ostream& operator<<(const char* str)
{
while(*str != '\0')
Putchar(*str++);
return *this;
}
} out;

inline void chkmin(int &A, int &B) { A = min(A, B);}
inline void chkmax(int &A, int &B) { A = max(A, B);}

const int Maxm = 4e5 + 10, Maxn = 2e5 + 10;

vector <pii> g[Maxn];

struct node{
int u, v, h;
bool operator < (const node A) const{ return h > A.h;}
}Map[Maxm];

int n, m, Q, K, S, ans;
int root[Maxn], dis[Maxn];

namespace CMT{
#define mid ((l + r) >> 1)
int ls[Maxn << 6], rs[Maxn << 6], cnt, root[Maxm << 2], Tim;
struct TRE{
int fa, siz, dis;
}tre[Maxn << 6];
inline void build(int &rt, int l, int r){
rt = ++cnt;
if(l == r) {tre[rt].fa = l, tre[rt].siz = 1, tre[rt].dis = dis[l]; return;}
build(ls[rt], l, mid);
build(rs[rt], mid + 1, r);
}
inline void Init(){ cnt = 0; build(root[1], 1, n); Tim = 0;}
inline int Query(int Begin, int rt, int l, int r, int pos){
if(l == r){return tre[rt].fa == l ? rt : Query(Begin, Begin, 1, n, tre[rt].fa);}
if(mid >= pos) return Query(Begin, ls[rt], l, mid, pos);
else return Query(Begin, rs[rt], mid + 1, r, pos);
}
inline void Modify_fa(int grt, int &rt, int l, int r, int pos, int fa){
rt = ++cnt, ls[rt] = ls[grt], rs[rt] = rs[grt], tre[rt] = tre[grt];
if(l == r) { tre[rt].fa = fa; return;}
if(mid >= pos) Modify_fa(ls[grt], ls[rt], l, mid, pos, fa);
else Modify_fa(rs[grt], rs[rt], mid + 1, r, pos, fa);
}
inline void Modify(int grt, int &rt, int l, int r, int pos, int siz, int Dis){
rt = ++cnt, ls[rt] = ls[grt], rs[rt] = rs[grt], tre[rt] = tre[grt];
if(l == r){
tre[rt].siz += siz, chkmin(tre[rt].dis, Dis);
return;
}
if(mid >= pos) Modify(ls[grt], ls[rt], l, mid, pos, siz, Dis);
else Modify(rs[grt], rs[rt], mid + 1, r, pos, siz, Dis);
}
inline int link(int u, int v){
register int rfu = Query(root[Tim << 1 | 1], root[Tim << 1 | 1], 1, n, u);
register int rfv = Query(root[Tim << 1 | 1], root[Tim << 1 | 1], 1, n, v);
if(tre[rfu].fa == tre[rfv].fa) return 0;
Tim++;
if(tre[rfu].siz > tre[rfv].siz) swap(rfu, rfv);
Modify_fa(root[(Tim << 1) - 1], root[Tim << 1], 1, n, tre[rfu].fa, tre[rfv].fa);
Modify(root[Tim << 1], root[Tim << 1 | 1], 1, n, tre[rfv].fa, tre[rfu].siz, tre[rfu].dis);
return Tim << 1 | 1;
}
inline LL Query_dis(int Time, int v){
return tre[Query(root[Time], root[Time], 1, n, v)].dis;
}
#undef mid
}

bitset<Maxn> vis, clean;

inline void dijstra(){
priority_queue <pii> Q;
memset(dis, 0x3f, sizeof dis);
vis = clean;
Q.push(mp(0, 1));
dis[1] = 0;
while(!Q.empty()){
register int now = Q.top().snd;
Q.pop();
if(vis[now]) continue;
vis[now] = 1;
for (register int i = g[now].size() - 1; i >= 0; --i){
register int nxt = g[now][i].fst;
if(dis[nxt] > dis[now] + g[now][i].snd)
dis[nxt] = dis[now] + g[now][i].snd,
Q.push(mp(-dis[nxt], nxt));
}
}
}
set<pii> Tim;
inline void work(int &T){
n = read(), m = read(), ans = 0;
for (register int i = 1; i <= m; ++i){
register int u = read(), v = read(), w = read(), h = read();
Map[i] = (node){u, v, h};
g[u].push_back(mp(v, w));
g[v].push_back(mp(u, w));
}
dijstra();
sort(Map + 1, Map + 1 + m);
CMT::Init();
for (register int i = 1; i <= m; ++i){
register int u = Map[i].u, v = Map[i].v;
register int TIME = CMT::link(u, v);
if(TIME)
Tim.insert(mp(Map[i].h, -TIME));
}
Q = read(), K = read(), S = read();
Tim.insert(mp(S + 1, -1));
Tim.insert(mp(0, -(CMT::Tim << 1 | 1)));
while(Q--){
register int v = ((LL)read() + K * ans - 1) % n + 1;
register int p = ((LL)read() + K * ans) % (S + 1);
pii Time = *Tim.upper_bound(mp(p, 1e9));
ans = CMT::Query_dis(-Time.snd, v);
out << ans << '\n';
}
if(T) Tim.clear();
if(T) for (register int i = 1; i <= n; ++i) g[i].clear();
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int T = read();
while(T--) work(T);
return 0;
}