zoukankan      html  css  js  c++  java
  • 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
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    缓存 memcached 与 redis
    爬了个爬(三)Scrapy框架
    算法 ----- 希尔排序
    算法 ----- 计数排序
    hibernate多对一单向外键
    HIBERNATE一对一双向外键联合主键关联
    hibernate一对一双向外键关联
    Hibernate一对一单向外键关联
    Hibernate关系级别注解
    Java在mysql插入数据的时候的乱码问题解决
  • 原文地址:https://www.cnblogs.com/zxytxdy/p/12834847.html
Copyright © 2011-2022 走看看