听说这是个 Kruskal 重构树的练习题,于是往这方面想。
先预处理出最短路 \(dis\)。
然后建出 Kruskal 重构树(枚举的边按照 \(h\) 从大到小排序)。
然后对于一个询问 \(v,p\)。
\(v\) 不断向上跳,直到海拔 \( 为止,然后这个点的子树内的点都可到达。
于是我们可以找到这个子树内 \(dis\) 最小的点就是答案。
于是这题就做完了。
最短路用 \(\text{Dij}\) 或者 \(\text{SPFA}\)。
Kruskal 重构树用并查集
\(v\) 向上跳用倍增。
子树内 \(dis\) 最小的点可以 ST 表或者线段树。
差不多 10:00 开始写的。
现在是 11:39,我还没调出来,我看看我什么时候调出来。
现在是 14:45,中间睡了两个小时的午觉,我调完了。
/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include
#include
#include
#include
#include
#include
#define int long long
#define orz cout<<"lkp AK IOI!"< 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< 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: "<> 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<= 0; --i) {
// cout<<"jump: "< p) {
// cout<
好久没调过这么长的代码了,记得上次调的时候还是在上次。
这道题好像就是 SPFA 之墓,所以我用的 Dij。/cy
ST 表虽然没怎么用过,但显然比线段树好写。
码力变强之后调题更快了,对于一些板子更自信了,所以我错哪了呢?
- 定义了
lst
没有用;
- 解密的公式写错了;
- 题意理解错了,海拔严格高于才能走,代码一开始写的是不低于。