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;
}
原文地址:https://www.cnblogs.com/wutongtong3117/p/7673373.html