Bzoj3545:Peaks

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\n",ans);
    }
    return 0;
}
posted @ 2020-05-06 10:30  zhaoxiaoyun  阅读(124)  评论(0编辑  收藏  举报