听说这是个 Kruskal 重构树的练习题,于是往这方面想。
先预处理出最短路 (dis)。
然后建出 Kruskal 重构树(枚举的边按照 (h) 从大到小排序)。
然后对于一个询问 (v,p)。
(v) 不断向上跳,直到海拔 (<p) 为止,然后这个点的子树内的点都可到达。
于是我们可以找到这个子树内 (dis) 最小的点就是答案。
于是这题就做完了。
最短路用 ( ext{Dij}) 或者 ( ext{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<<"
";
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<<"
";
// cout<<"edge: "<<Cnt<<" "<<vf<<"
";
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<<" "<<"
";
int x = v;
for(int i = 20; i >= 0; --i) {
// cout<<"jump: "<<x<<" "<<i<<" "<<fath[x][i]<<" "<<H[x][i]<<"
";
if(fath[x][i] && H[x][i] > p) {
// cout<<i<<"
";
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<<" "<<"
";
lst = min(Dis[l][Log2[len]], Dis[r - (1 << Log2[len]) + 1][Log2[len]]);
printf("%lld
", lst);
}
}
return 0;
}
好久没调过这么长的代码了,记得上次调的时候还是在上次。
这道题好像就是 SPFA 之墓,所以我用的 Dij。/cy
ST 表虽然没怎么用过,但显然比线段树好写。
码力变强之后调题更快了,对于一些板子更自信了,所以我错哪了呢?
- 定义了
lst
没有用; - 解密的公式写错了;
- 题意理解错了,海拔严格高于才能走,代码一开始写的是不低于。