zoukankan      html  css  js  c++  java
  • 【bzoj3545/bzoj3551】[ONTAK2010]Peaks/加强版 Kruskal+树上倍增+Dfs序+主席树

    bzoj3545

    题目描述

    在Bytemountains有N座山峰,每座山峰有他的高度h_i。有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1。 

    输入

    第一行三个数N,M,Q。
    第二行N个数,第i个数为h_i
    接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径。
    接下来Q行,每行三个数v x k,表示一组询问。 

    输出

    对于每组询问,输出一个整数表示答案。 

    样例输入

    10 11 4
    1 2 3 4 5 6 7 8 9 10
    1 4 4
    2 5 3
    9 8 2
    7 8 10
    7 1 4
    6 7 1
    6 4 8
    2 1 5
    10 8 10
    3 4 7
    3 4 6
    1 5 2
    1 5 6
    1 5 8
    8 9 2

    样例输出

    6
    1
    -1
    8

    bzoj3551

    输入

    接下来Q行,每行三个数v x k,表示一组询问。v=v xor lastans,x=x xor lastans,k=k xor lastans。如果lastans=-1则不变。


    题解

    Kruskal+倍增算法+dfs序+主席树

    p3445允许离线,所以还可以用Treap启发式合并。

    然而p3451强制在线,这样做肯定不行。

    首先肯定是先Kruskal求最小生成树,而一般的最小生成树也无法表示任意两点间距离最大值。

    这里用到一个黑科技:Kruskal重构树。

    在求最小生成树时,不直接连接两个节点,而是将两个节点的祖先连接到一个新的节点上。

    这个新的节点与这两个节点之间的边权就是边的长度。

    这有什么好处?

    上图是按照这种方式重构的一棵树,其中节点1~10为原节点,11~19为新加节点。

    可以看出,从下至上的路径,边权是单调不减的(看作点权即大根堆)。

    那么,想要寻找路径小于等于x的所有能到达的点,就可以从最下方的原节点向上查找最远的路径小于x的点,这个点的子树就是所求的点集合。

    题目要求这个点集合里第k大的,需要使这些点连续出现,于是想到Dfs序。

    我们可以构建一个Dfs序,然后使用主席树来维护并查询第k大。

    需要注意的是这两道题都需要读入优化,否则会TLE。

    以下为p3545的代码,若为p3551,只需要在询问时修改一小部分即可。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    struct node
    {
        int x , y , z;
    }e[500005];
    struct data
    {
        int num , rank;
    }a[200005];
    int f[200005] , log[200005] , fa[200005][18] , dis[200005][18] , deep[200005];
    int head[200005] , to[200005] , val[200005] , next[200005] , cnt;
    int lp[200005] , rp[200005] , pl , q[400005] , ref[200005] , top;
    int ls[6000005] , rs[6000005] , si[6000005] , root[400005] , tot;
    inline int read()
    {
        int num = 0; char ch;
        while(ch < '0' || ch > '9') ch = getchar();
        while(ch >= '0' && ch <= '9') num = num * 10 + ch - '0' , ch = getchar();
        return num;
    }
    bool cmp1(data a , data b)
    {
        return a.num < b.num;
    }
    bool cmp2(data a , data b)
    {
        return a.rank < b.rank;
    }
    bool cmp3(node a , node b)
    {
        return a.z < b.z;
    }
    int find(int x)
    {
        return x == f[x] ? x : f[x] = find(f[x]);
    }
    void add(int x , int y , int z)
    {
        to[++cnt] = y;
        val[cnt] = z;
        next[cnt] = head[x];
        head[x] = cnt;
    }
    void dfs(int x)
    {
        int i;
        lp[x] = ++pl;
        q[pl] = a[x].num;
        for(i = head[x] ; i ; i = next[i])
        {
            if(to[i] != fa[x][0])
            {
                fa[to[i]][0] = x;
                deep[to[i]] = deep[x] + 1;
                dfs(to[i]);
            }
        }
        rp[x] = ++pl;
    }
    void init(int n)
    {
        int i , j;
        for(i = 2 ; i <= n ; i ++ )
            log[i] = log[i >> 1] + 1;
        for(i = 1 ; i <= log[n] ; i ++ )
            for(j = 1 ; j <= n ; j ++ )
                if(deep[j] >= (1 << i))
                    fa[j][i] = fa[fa[j][i - 1]][i - 1] , dis[j][i] = max(dis[j][i - 1] , dis[fa[j][i - 1]][i - 1]);
    }
    void pushup(int x)
    {
        si[x] = si[ls[x]] + si[rs[x]];
    }
    void ins(int x , int &y , int l , int r , int p)
    {
        y = ++tot;
        if(l == r)
        {
            si[y] = si[x] + 1;
            return;
        }
        int mid = (l + r) >> 1;
        if(p <= mid) rs[y] = rs[x] , ins(ls[x] , ls[y] , l , mid , p);
        else ls[y] = ls[x] , ins(rs[x] , rs[y] , mid + 1 , r , p);
        pushup(y);
    }
    int query(int x , int y , int l , int r , int p)
    {
        if(l == r) return ref[l];
        int mid = (l + r) >> 1;
        if(si[ls[y]] - si[ls[x]] >= p) return query(ls[x] , ls[y] , l , mid , p);
        else return query(rs[x] , rs[y] , mid + 1 , r , p - si[ls[y]] + si[ls[x]]);
    }
    int main()
    {
        int n , m , qu , i , tx , ty , v , x , k;
        n = read() , m = read() , qu = read();
        for(i = 1 ; i <= n ; i ++ )
            a[i].num= read() , a[i].rank = i;
        sort(a + 1 , a + n + 1 , cmp1);
        ref[0] = 0x8000000;
        for(i = 1 ; i <= n ; i ++ )
        {
            if(a[i].num != ref[top]) ref[++top] = a[i].num;
            a[i].num = top;
        }
        sort(a + 1 , a + n + 1 , cmp2);
        for(i = 1 ; i <= n << 1 ; i ++ ) 
            f[i] = i;
        for(i = 1 ; i <= m ; i ++ )
            e[i].x = read() , e[i].y = read() , e[i].z = read();
        sort(e + 1 , e + m + 1 , cmp3);
        for(i = 1 ; i <= m ; i ++ )
        {
            tx = find(e[i].x) , ty = find(e[i].y);
            if(tx != ty)
            {
                f[tx] = f[ty] = ++n;
                fa[tx][0] = fa[ty][0] = n;
                dis[tx][0] = dis[ty][0] = e[i].z;
                add(n , tx , e[i].z);
                add(n , ty , e[i].z);
            }
        }
        dfs(n);
        init(n);
        for(i = 1 ; i <= pl ; i ++ )
        {
            if(!q[i]) root[i] = root[i - 1];
            else ins(root[i - 1] , root[i] , 1 , top , q[i]);
        }
        while(qu -- )
        {
            v = read() , x = read() , k = read();
            tx = v;
            for(i = log[deep[v]] + 1 ; i >= 0 ; i -- )
                if(deep[tx] >= (1 << i) && dis[tx][i] <= x)
                    tx = fa[tx][i];
            printf("%d
    " , si[root[rp[tx]]] - si[root[lp[tx]]] >= k ? query(root[lp[tx]] , root[rp[tx]] , 1 , top , si[root[rp[tx]]] - si[root[lp[tx]]] - k + 1) : -1);
        }
        return 0;
    }
  • 相关阅读:
    当Django模型迁移时,报No migrations to apply 问题时
    django--各个文件的含义
    django--创建项目
    1013. Battle Over Cities (25)
    1011. World Cup Betting (20)
    1009. Product of Polynomials (25)
    1007. Maximum Subsequence Sum (25)
    1006. Sign In and Sign Out (25)
    1008. Elevator (20)
    1004. Counting Leaves (30)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6293547.html
Copyright © 2011-2022 走看看