Bzoj3545:Peaks
题意:
给定一张图,有(n)和点和(m)条边,每次询问给定(v,x,k),表示询问从(v)点开始不经过权值大于(x)的边能到达的第(k)大的点是什么。
思路:
Kruskal重构树+LCA+主席树。
首先我们需要找一种方法,快速地找到一个点在不经过权值大于(x)的边能到达的点的集合。
对于一个点来说,能经过的边权小于(x)的边能到达的点是一定的。
若一个点通过某一条路径可达,那么走最小生成树上的边也一定可达。
建Kruskal重构树。
这不算是一种数据结构,算是一种思想。
在Kruskal建树的过程中,对于两个可以合并的点对((x,y)),我们这样建立一个新的结点(T),让(T)成为((x,y))的父亲,同时给(T)设置点权为((x,y))之间的边权。
可以发现这是一个二叉树(大根堆),而且叶子节点都是原图中的节点。
回到题目,当我们对一个点进行询问的时候,我们可以倍增的求出从当前节点往上走点权小于等于(x)的最大值,然后利用主席树维护子树第(k)大即可。
维护子树第(K)大具体实现是先对重构树的叶子求出(dfs)序后维护。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
struct edge{
int x,y,z;
}e[maxn];
int ls[maxn*10], rs[maxn*10], s[maxn*10];
int siz,f[maxn][20],dfn[maxn],mn[maxn],mx[maxn];
int idx, n, m, q, val[maxn], rt[maxn], a[maxn], fa[maxn];
int cnt;
int head[maxn], nex[maxn<<1], ver[maxn<<1], tot;
void add_edge(int x,int y){
ver[++tot] = y; nex[tot] = head[x]; head[x] = tot;
}
bool cmp(edge a,edge b){
return a.z < b.z;
}
int get_fa(int x){
if(x == fa[x]) return x;
return fa[x] = get_fa(fa[x]);
}
void dfs(int x)
{
//叶子节点
if(x <= n)
{
mn[x] = mx[x] = ++idx;
dfn[idx] = x;
}
else mn[x] = n+1;
for(int i = 1; i < 20; i++) f[x][i] = f[f[x][i-1]][i-1];
for(int i = head[x]; i; i = nex[i])
{
int y = ver[i];
f[y][0] = x;
dfs(y);
mn[x]=min(mn[x], mn[y]);
mx[x]=max(mx[x], mx[y]);
}
}
void ins(int &p,int q,int l,int r,int x)
{
p = ++siz;
ls[p] = ls[q]; rs[p] = rs[q];
s[p] = s[q]+1;
if(l==r) return;
int mid = (l+r)>>1;
if(x <= mid) ins(ls[p], ls[q], l, mid, x);
else ins(rs[p], rs[q], mid+1, r, x);
}
int get(int x,int w)
{
for(int i=19;i>=0;i--)if(f[x][i] && val[f[x][i]] <= w) x = f[x][i];
return x;
}
inline int query(int p, int q, int l, int r, int x)
{
if(s[p]-s[q] < x) return -1;
if(l == r) return l;
int mid = (l+r)>>1;
if(s[rs[p]]-s[rs[q]] >= x) return query(rs[p], rs[q], mid+1, r, x);
else return query(ls[p], ls[q], l, mid, x-s[rs[p]]+s[rs[q]]);
}
int main()
{
scanf("%d%d%d", &n, &m, &q); cnt = n;
for(int i=1;i<=n;i++)
{
scanf("%d", &val[i]);
a[i] = val[i];
}
sort(a+1, a+n+1);
int len = unique(a+1, a+n+1)-a-1;
for(int i = 1; i <= n+n; i++) fa[i] = i;
for(int i = 1; i <= n; i++) val[i] = lower_bound(a+1, a+len+1, val[i])-a;
for(int i = 1; i <= m; i++) scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].z);
//建kruskal重构树
sort(e+1, e+m+1, cmp);
for(int i = 1; i <= m; i++)
{
int fx = get_fa(e[i].x), fy = get_fa(e[i].y);
if(fx!=fy)
{
val[++cnt]=e[i].z;
add_edge(cnt, fx), add_edge(cnt, fy);
fa[fx] = fa[fy] = cnt;
}
}
dfs(cnt);
for(int i = 1; i <= n; i++)
ins(rt[i], rt[i-1], 1, n, val[dfn[i]]);
int v, x, k, ans = 0;
while(q--)
{
scanf("%d%d%d", &v, &x, &k);
int p = get(v, x);
ans = query(rt[mx[p]], rt[mn[p]-1], 1, n, k);
if(ans > -1) ans = a[ans];
printf("%d
",ans);
}
return 0;
}