• bzoj3551 [ONTAK2010]Peaks加强版(Kruskal重构树+主席树)


    Description

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

    Input

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

    Output

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

    Sample Input
    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

    Sample Output
    6
    10
    9
    -1

    HINT

    【数据范围】
    N<=10^5, M,Q<=5*10^5,h_i,c,x<=10^9。

    分析:
    Kruskal树+主席树
    一开始我以为要把所有的边都建到kruskal树上
    但是边好多,而且好像办不到啊
    后来仔细一想
    题目要求是在<=x的边上移动

    只要找到最小生成树就可以了

    也许会有这样的疑问
    一条边<=x
    但是ta连接的两个点一定在一个联通快内了,那么x就连不上了,这怎么办呢
    没有做到在所有<=x的边上移动啊

    但是仔细考虑一下我们的命题
    x连接的两个点已经联通了,那加不加x这条边都没有影响啊

    整理一下代码思路吧:
    一.如果要用数组存储主席树的话
    一上来就要对高度进行离散(神烦)
    二.一个很普通的kruskal构建,我这里用的是自己yy的做法
    三.倍增维护爸爸和权值
    **四.**dfs 这个dfs很鬼,只有是叶子结点的才能算入dfs序
    五.构建主席树
    因为我几乎不会主席树,所以找的网上dalao的代码(纯结构体,我喜欢)
    其实就是把每个高度扔进树里,每扔一个数root+1表示时间的推移
    因为我们是按照dfs序来进行的构建,所以一棵子树的root一定是一个区间
    主席树节点中要记录左儿子,右儿子和树中的元素数目
    六.处理询问

    注意,初始ans=1

    询问的处理就像线段树一样,
    因为提问的是第k高,所以就是从后往前数的第k个
    如果区间右儿子的值相减大于x,那我们直接进入x
    否则进入左儿子,但是x的值要相应的处理

    -tree[tree[y].r].sz+tree[tree[x].r].sz

    tip

    数组一定要开够了
    主席树的空间nlogn,200000*20

    这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    
    using namespace std;
    
    const int N=200005;
    struct node{
        int x,y,v;
    };
    node e[500010];
    struct nd{
        int x,y,nxt;
    };
    nd way[N];
    int mx[N][20],lg,f[N][20];
    int n,m,h[N],Q,fa[N],tot=0,st[N],in[N],out[N],cnt=0,tt,H,hh[N],root[N];
    int a[N],total=0,ans=0;
    struct nod{
        int l,r,sz;
    };
    nod tree[4000000];
    struct point{
        int x,bh;
    };
    point po[N];
    
    void add(int u,int w)
    {
        tot++;
        way[tot].x=u;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
    }
    
    int cmp2(const point &a,const point &b){return a.x<b.x;}
    int cmp(const node &a,const node &b){return a.v<b.v;}
    
    int find(int a)
    {
        if (fa[a]!=a) fa[a]=find(fa[a]);
        return fa[a];
    }
    
    void kruskal()
    {
        int i,j,o=0;
        tt=n;
        sort(e+1,e+1+m,cmp);
        for (int i=1;i<=n+n;i++) fa[i]=i; 
        for (i=1;i<=m;i++)
        {
            int f1=find(e[i].x);
            int f2=find(e[i].y);
            if (f1!=f2)
            {
                fa[f1]=fa[f2]=++tt;  //并查集 
                f[f1][0]=f[f2][0]=tt;  //papa 
                mx[f1][0]=mx[f2][0]=e[i].v;   //最大值 
                add(tt,f1);add(tt,f2);
                o++;
            }
            if (o==n-1) break;
        }
    }
    
    void dfs(int x)
    {
        in[x]=a[0];
        if (st[x]==0) a[++a[0]]=x;   //dfs
        for (int i=st[x];i;i=way[i].nxt)
            dfs(way[i].y);
        out[x]=a[0];
    }
    
    void insert(int x,int &y,int l,int r,int z)
    {
        y=++total;   //total节点计数
        if (l==r)
        {
            tree[y].sz=tree[x].sz+1;
            return;
        } 
        int mid=(l+r)>>1;
        if (z<=mid) tree[y].r=tree[x].r,insert(tree[x].l,tree[y].l,l,mid,z);
        else tree[y].l=tree[x].l,insert(tree[x].r,tree[y].r,mid+1,r,z);
        tree[y].sz=tree[tree[y].l].sz+tree[tree[y].r].sz;
    }
    
    void ask(int x,int y,int l,int r,int k)
    {
        if (l==r) 
        {
            ans=hh[l];  //山峰高度
            return; 
        }
        int mid=(l+r)>>1;
        if (tree[tree[y].r].sz-tree[tree[x].r].sz>=k) return ask(tree[x].r,tree[y].r,mid+1,r,k);
        else return ask(tree[x].l,tree[y].l,l,mid,k-tree[tree[y].r].sz+tree[tree[x].r].sz);
    //因为提问的是第k高,所以就是从后往前数的第k个 
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&Q);
        for (int i=1;i<=n;i++) scanf("%d",&po[i].x),po[i].bh=i;
    
        sort(po+1,po+1+n,cmp2);
        H=0;
        hh[H]=-1;
        for (int i=1;i<=n;i++){
            if (hh[H]<po[i].x) hh[++H]=po[i].x;  //hh[H] H对应的数字 
            h[po[i].bh]=H;   //离散后的答案 
        }
    
        for (int i=1;i<=m;i++)
            scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
        kruskal();
        lg=log(tt)/log(2);
        for (int i=1;i<=lg;i++)   //倍增 
            for (int j=1;j<=tt;j++)
                f[j][i]=f[f[j][i-1]][i-1],
                mx[j][i]=max(mx[j][i-1],mx[f[j][i-1]][i-1]);
        dfs(tt);
    
        for (int i=1;i<=n;i++) insert(root[i-1],root[i],1,H,h[a[i]]);  //按照dfs序添加 
    
        for (int i=1;i<=Q;i++)
        {
            int u,w,z;
            scanf("%d%d%d",&u,&w,&z);
            u^=ans;w^=ans;z^=ans;
            int temp=u;
            for (int j=lg;j>=0;j--)  //找到符合条件的lca 
                if (f[temp][j]&&mx[temp][j]<=w)  //lca<=x
                   temp=f[temp][j];  //temp=lca-1
            if (out[temp]-in[temp]<z)
            {
                puts("-1");
                ans=0;
                continue;
            } 
            ask(root[in[temp]],root[out[temp]],1,H,z);
            //一开始还在奇怪为什么不是in[temp]-1,仔细想了一下在计算lca的时候我们已经少算了一个1了 
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    Linux基础篇之软件源码包安装
    11-1 网络协议和管理
    bash-2 httpd服务的源码编译安装脚本
    8-1 文本三级剑客之sed
    9-3 磁盘存储与分区
    9-2 yum,dnf和apt
    9-1 软件包管理
    bash-1 初始化CentOS系统的初始化脚本
    3-3 man手册介绍
    5-3 文件权限
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673373.html
Copyright © 2020-2023  润新知