世界不完美 所以才显得美丽



听说这是个 Kruskal 重构树的练习题,于是往这方面想。

先预处理出最短路 \(dis\)。

然后建出 Kruskal 重构树(枚举的边按照 \(h\) 从大到小排序)。

然后对于一个询问 \(v,p\)。

\(v\) 不断向上跳,直到海拔 \(<p\) 为止,然后这个点的子树内的点都可到达。

于是我们可以找到这个子树内 \(dis\) 最小的点就是答案。

于是这题就做完了。

最短路用 \(\text{Dij}\) 或者 \(\text{SPFA}\)。

Kruskal 重构树用并查集

\(v\) 向上跳用倍增。

子树内 \(dis\) 最小的点可以 ST 表或者线段树。

差不多 10:00 开始写的。

现在是 11:39,我还没调出来,我看看我什么时候调出来。

现在是 14:45,中间睡了两个小时的午觉,我调完了。

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 4e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct node {
int fr, to, w, h;
bool operator < (const node &b) const { return h > b.h; }
}E[MAXN];

struct Node {
int id, val;
bool operator < (const Node &b) const { return val > b.val;}
};

struct edge {
int to, w, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 1;

int n, m, Q, K, S, Cnt;
int val[MAXN], fa[MAXN], Log2[MAXN];
int dis[MAXN], Dis[MAXN][21];
int siz[MAXN], dfn[MAXN], fath[MAXN][22], H[MAXN][21];
bool vis[MAXN];

int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}

namespace Cut {
struct edge { int to, nxt; }e[MAXN << 1];
int head[MAXN], num_edge = 1;
int cnt = 0;
void add_edge(int from, int to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }
void dfs(int u, int fa) {
// cout<<u<<"\n";
siz[u] = 1, fath[u][0] = fa, dfn[u] = ++cnt;
H[u][0] = fa <= n ? INF : val[fa]; Dis[cnt][0] = dis[u];
for(int i = 1; i <= 20; ++i) {
fath[u][i] = fath[fath[u][i - 1]][i - 1];
H[u][i] = min(H[u][i - 1], H[fath[u][i - 1]][i - 1]);
}
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
dfs(v, u);
siz[u] += siz[v];
}
}
}

void add_edge(int from, int to, int w) { e[++num_edge] = (edge){to, w, head[from]}, head[from] = num_edge; }
void Dij() {
priority_queue<Node> q;
memset(dis, 0x3f, sizeof dis);
memset(vis, false, sizeof vis);
q.push((Node){1, 0}), dis[1] = 0;
while(!q.empty()) {
int u = q.top().id; q.pop();
if(vis[u]) continue;
vis[u] = true;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(dis[v] > dis[u] + e[i].w) {
dis[v] = dis[u] + e[i].w;
if(!vis[v]) q.push((Node){v, dis[v]});
}
}
}
}

int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void ExKruskal() {
sort(E + 1, E + m + 1);
for(int i = 1; i <= 2 * n; ++i) fa[i] = i;
for(int i = 1; i <= m; ++i) {
int u = E[i].fr, v = E[i].to, w = E[i].h;
int uf = find(u), vf = find(v);
if(uf != vf) {
fa[uf] = fa[vf] = ++Cnt;
// cout<<"edge: "<<Cnt<<" "<<uf<<" \n";
// cout<<"edge: "<<Cnt<<" "<<vf<<" \n";
Cut::add_edge(Cnt, uf), Cut::add_edge(Cnt, vf);
val[Cnt] = w;
if(Cnt == 2 * n - 1) break;
}
}
}

void Init() {
for(int i = 1; i <= 20; ++i) {
for(int j = 1; j + (1 << i) - 1 <= Cut::cnt; ++j) {
Dis[j][i] = min(Dis[j][i - 1], Dis[j + (1 << i - 1)][i - 1]);
}
}
}

void Clear() {
memset(head, false, sizeof head);
memset(Cut::head, false, sizeof Cut::head);
num_edge = Cut::num_edge = 1;
memset(Dis, 0x3f, sizeof Dis);
memset(val, false, sizeof val);
memset(fath, false, sizeof fath);
memset(H, false, sizeof H);
Cut::cnt = 0;
}

signed main()
{
int T = read();
for(int i = 2; i <= 400000; ++i) Log2[i] = Log2[i >> 1] + 1;
while(T--) {
Clear();
n = read(), m = read();
Cnt = n;
for(int i = 1; i <= m; ++i) {
E[i].fr = read(), E[i].to = read(), E[i].w = read(), E[i].h = read();
add_edge(E[i].fr, E[i].to, E[i].w), add_edge(E[i].to, E[i].fr, E[i].w);
}
Dij();
ExKruskal();
Cut::dfs(Cnt, 0);
Init();
// cout<<"dis: ";
// for(int i = 1; i <= n; ++i) cout<<dis[i]<<" "; puts("");
// for(int i = 1; i <= Cut::cnt; ++i) cout<<dfn[i]<<" "; puts("");
// for(int i = 1; i <= Cut::cnt; ++i) cout<<Dis[i][0]<<" "; puts("");
Q = read(), K = read(), S = read();
for(int i = 1, v, p, lst = 0; i <= Q; ++i) {
v = read(), p = read();
v = (v + K * lst - 1) % n + 1, p = (p + K * lst) % (S + 1);
// cout<<"v p: "<<v<<" "<<p<<" "<<"\n";
int x = v;
for(int i = 20; i >= 0; --i) {
// cout<<"jump: "<<x<<" "<<i<<" "<<fath[x][i]<<" "<<H[x][i]<<" \n";
if(fath[x][i] && H[x][i] > p) {
// cout<<i<<" \n";
x = fath[x][i];
}
}
int len = siz[x], l = dfn[x], r = dfn[x] + siz[x] - 1;
// cout<<x<<" "<<fath[x][0]<<" "<<H[x][0]<<" "<<l<<" "<<r<<" "<<"\n";
lst = min(Dis[l][Log2[len]], Dis[r - (1 << Log2[len]) + 1][Log2[len]]);
printf("%lld\n", lst);
}
}
return 0;
}


好久没调过这么长的代码了,记得上次调的时候还是在上次。

这道题好像就是 SPFA 之墓,所以我用的 Dij。/cy

ST 表虽然没怎么用过,但显然比线段树好写。

码力变强之后调题更快了,对于一些板子更自信了,所以我错哪了呢?

  • 定义了 ​​lst​​ 没有用;
  • 解密的公式写错了;
  • 题意理解错了,海拔严格高于才能走,代码一开始写的是不低于。