P4197 Peaks
知识点: 线段树合并,主席树,Kruscal 重构树
原题面 Luogu
题意简述
给定一张 \(n\) 个点,\(m\) 条边的图,点有点权,边有边权。
每次询问给定参数 \(v, x, k\),求从节点 \(v\) 出发,只经过边权 \(\le x\) 的边,能到达的所有节点中,第 \(k\) 大的点权。
\(1\le n\le 10^5\),\(0\le m, q\le 5\times 10^5\),点权边权 \(\le 10^9\)。
分析题意
在线
这是一个显然的在线做法。
只能经过边权 \(\le x\) 的边,套路地考虑 Kruscal 重构树。
边权 \(\rightarrow\) 点权,重构树上的点权值 自根向叶子递减。
对于每一个询问,从 \(v\) 倍增到点权值 \(\le x\) 的,点权最大的祖先 \(u\)。
显然,\(u\) 的子树中所有节点的点权 \(\le x\)。
从 \(v\) 出发,可到达 \(u\) 子树中所有节点。
答案即该子树中所有 叶节点,即原图中的节点的第 \(k\) 大点权。
子树查询问题,考虑处理出 \(dfs\) 序,子树中的点被转化为序列上的一段连续区间。
静态区间第 \(k\) 大值问题,主席树查询即可。
复杂度 \(O(m\log m + q\log n)\)。
离线
这是一个显然的离线做法。
离线询问,将询问按边权限制 \(x\) 升序排序。
枚举询问时考虑边权比上个询问限制 更大的边的影响。
问题变为每次向图中添加一些边,并查询给定节点所在连通块点权的 第 \(k\) 大值。
变成了这题:bzoj4399. 魔法少女LJJ,线段树合并 + 并查集简单维护即可。
代码实现
//知识点:权值线段树,线段树合并,并查集
/*
By:Luckyblock
*/
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
#define ll long long
#define ls (t[now].lson)
#define rs (t[now].rson)
const int kMaxn = 5e5 + 10;
const int kInf = 1e9 + 5;
//=============================================================
struct Edge {
int u, v, c;
} e[kMaxn];
struct Query {
int v, x, k, id;
} q[kMaxn];
struct SegmentTree {
int lson, rson, sum;
} t[kMaxn << 5];
int n, m, qnum, fa[kMaxn];
int node_num, root[kMaxn], ans[kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void GetMax(int &fir, int sec) {
if (sec > fir) fir = sec;
}
void GetMin(int &fir, int sec) {
if (sec < fir) fir = sec;
}
bool CompareEdge(Edge fir, Edge sec) {
return fir.c < sec.c;
}
bool CompareQuery(Query fir, Query sec) {
return fir.x < sec.x;
}
int Find(int x) {
return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
void Update(int now) {
t[now].sum = t[ls].sum + t[rs].sum;
}
void Insert(int &now, int L, int R, int pos, int val) {
if (! now) now = ++ node_num;
if (L == R) {
t[now].sum += val;
return ;
}
int mid = (L + R) >> 1;
if (pos <= mid) Insert(ls, L, mid, pos, val);
else Insert(rs, mid + 1, R, pos, val);
Update(now);
}
int Merge(int now, int y, int L, int R) {
if (! now || ! y) return now + y;
if (L == R) {
t[now].sum += t[y].sum;
return now;
}
int mid = (L + R) >> 1;
ls = Merge(ls, t[y].lson, L, mid);
rs = Merge(rs, t[y].rson, mid + 1, R);
Update(now);
return now;
}
void Union(int x, int y) {
int r1 = Find(x), r2 = Find(y);
if (r1 == r2) return ;
fa[r1] = r2;
root[r2] = Merge(root[r2], root[r1], 1, kInf);
}
void Prepare() {
n = read(), m = read(), qnum = read();
for (int i = 1; i <= n; ++ i) Insert(root[i], 1, kInf, read(), 1);
for (int i = 1; i <= m; ++ i) {
int u = read(), v = read(), c = read();
e[i] = (Edge) {u, v, c};
}
for (int i = 1; i <= qnum; ++ i) {
int v = read(), x = read(), k = read();
q[i] = (Query) {v, x, k, i};
}
for (int i = 1; i <= n; ++ i) fa[i] = i;
std :: sort(e + 1, e + m + 1, CompareEdge);
std :: sort(q + 1, q + qnum + 1, CompareQuery);
}
int Query(int now, int L, int R, int k) {
if (L == R) return L;
int size = t[rs].sum, mid = (L + R) >> 1;
if (k <= size) return Query(rs, mid + 1, R, k);
else return Query(ls, L, mid, k - size);
}
//=============================================================
int main() {
Prepare();
for (int i = 1, j = 0; i <= qnum; ++ i) {
int v = q[i].v, x = q[i].x, k = q[i].k;
for (; j <= m && e[j].c <= x; ++ j) Union(e[j].u, e[j].v);
if (t[root[Find(v)]].sum < k) {
ans[q[i].id] = - 1;
} else {
ans[q[i].id] = Query(root[Find(v)], 1, kInf, k);
}
}
for (int i = 1; i <= qnum; ++ i) printf("%d\n", ans[i]);
return 0;
}
没写完的 Kruscal 重构树在线做法。
调不出来弃了!
//知识点:Kruscal重构树,主席树
/*
By:Luckyblock
*/
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
#define ll long long
#define ls (t[now].lson)
#define rs (t[now].rson)
const int kMaxn = 5e2 + 10;
const int kInf = 1e9 + 2077;
//=============================================================
struct Edge {
int u, v, w, ne;
} e[kMaxn << 3], e1[kMaxn];
struct SegmentTree {
int lson, rson, size;
} t[kMaxn << 5];
int n, m, q, edge_num, node_num, h[kMaxn], head[kMaxn];
int Dfn, root, bel[kMaxn], hard[kMaxn], dep[kMaxn], dfn[kMaxn], size[kMaxn], fa[kMaxn][25];
int tree_node_num, data[kMaxn], val[kMaxn], Root[kMaxn];
int map[kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void GetMax(int &fir, int sec) {
if (sec > fir) fir = sec;
}
void GetMin(int &fir, int sec) {
if (sec < fir) fir = sec;
}
bool Compare(Edge fir, Edge sec) {
return fir.w < sec.w;
}
void AddEdge(int u, int v) {
e[++ edge_num].v = v, e[edge_num].u = u;
e[edge_num].ne = head[u], head[u] = edge_num;
}
int Find(int x) {
return bel[x] == x ? x : bel[x] = Find(bel[x]);
}
void Unico(int x, int y, int w) {
int r1 = Find(x), r2 = Find(y);
root = ++ node_num;
bel[r1] = root, bel[r2] = root; //
h[root] = 0; hard[root] = w;
AddEdge(root, r1), AddEdge(root, r2);
}
void Dfs(int u) {
dfn[u] = ++ Dfn,
data[Dfn] = val[Dfn] = h[u];
size[u] = 1;
for (int i = 1; (1 << i) <= dep[u]; ++ i) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for (int i = head[u]; i; i = e[i].ne) {
dep[e[i].v] = dep[u] + 1, fa[e[i].v][0] = u;
Dfs(e[i].v);
size[u] += size[e[i].v];
}
}
void Build(int &now, int L, int R) {
now = ++ tree_node_num;
if (L >= R) return ;
int mid = (L + R) >> 1;
Build(ls, L, mid), Build(rs, mid + 1, R);
}
void Update(int pre, int &now, int L, int R, int value) {
now = ++ tree_node_num;
t[now].size = t[pre].size + 1;
ls = t[pre].lson, rs = t[pre].rson;
if (L >= R) return ;
int mid = (L + R) >> 1;
if (value > mid) Update(rs, rs, mid + 1, R, value);
else Update(ls, ls, L, mid, value);
}
int Query(int l, int r, int L, int R, int key) {
if(L == R) return L;
int sz = t[t[r].lson].size - t[t[l].lson].size; //
int mid = (L + R) >> 1;
if(key <= sz) return Query(t[l].lson, t[r].lson, L, mid, key);
return Query(t[l].rson, t[r].rson, mid + 1, R, key - sz);
}
//=============================================================
int main() {
node_num = n = read(), m = read(), q = read();
for (int i = 1; i <= n; ++ i) h[i] = read();
for (int i = 1; i <= m; ++ i) {
e1[i].u = read(), e1[i].v = read(), e1[i].w = read();
}
std ::sort(e1 + 1, e1 + m + 1, Compare);
for (int i = 1; i <= n + m; ++ i) bel[i] = i;
for (int i = 1; i <= m; ++ i) {
int u = e1[i].u, v = e1[i].v, w = e1[i].w;
if (Find(u) == Find(v)) continue ;
Unico(u, v, w);
}
// for (int i = 1; i <= edge_num; ++ i) {
// printf("%d ", e[i].u);
// printf("%d\n", e[i].v);
// }
// for (int i = 1; i <= node_num; ++ i) printf("%d ", hard[i]);
// printf("\n\n\n");
Dfs(root);
// for (int i = 1; i <= node_num; ++ i) printf("%d ", val[i]);
// printf("\n\n\n");
std :: sort(data + 1, data + node_num + 1);
int lth = std :: unique(data + 1, data + node_num + 1) - data - 1;
for (int i = 1; i <= node_num; ++i) {
int ori = val[i];
val[i] = std :: lower_bound(data + 1, data + lth + 1, val[i]) - data;
map[val[i]] = ori;
}
// for (int i = 1; i <= node_num; ++ i) printf("%d ", val[i]);
// printf("\n\n\n");
Build(Root[0], 1, node_num);
for (int i = 1; i <= node_num; ++ i) {
Update(Root[i - 1], Root[i], 1, node_num, val[i]);
}
for (int i = 1; i <= q; ++ i) {
int v = read(), x = read(), k = read(), rt = v;
for (int j = 20; j >= 0; j --) {
if (fa[rt][j] && hard[fa[rt][j]]<= x) {
rt = fa[rt][j];
}
}
int ans = Query(Root[dfn[rt] - 1], Root[dfn[rt] + size[rt] - 1], 1, node_num, size[rt] - k);
printf("%d\n", map[ans - 1] == 0 ? - 1 : map[ans - 1]);
}
return 0;
}
作者@Luckyblock,转载请声明出处。