WHU校赛2019 Online F. Climb(树上主席树)

WHU校赛2019 Online F. Climb

[没有链接]


题目

JBT is going to climb the mountain, but he was suddenly trapped into an unknown enchantment.
There are n mountains, connected by n−1 paths. There is only one road between any two mountains. Each mountain has a height h. In order to leave this enchantment, he has to answer m questions, for each question JBT should answer the height of the kth-highest mountain passed by on the road from the mountain u to the mountain v,and JBT cannot travel through the same mountain twice.
JBT is very worried that he will never be able to get out of this enchantment, so he wants you to help him.

Input

The first line contains one integer T indicate the number of tests.
The second line contains two integers n and m (1≤n,m≤105).
The third line contains n integers h1,h2,h3…hn (1≤hi≤105).
Each of the next n−1 lines contains two integers u and v (1≤u,v≤n), which means there is a road between ui and vi.
Each of the next m lines contains three integers u, v and k (1≤u,v,k≤n).

Output

For each of query, print the height of the kth-highest mountain in a single line.

If k is bigger than the distance between u and v, please output −1;

input

1
8 4
1 2 3 4 5 6 7 8
1 2
1 3
2 4
2 5
3 6
3 7
3 8
4 5 2
4 6 3
5 3 2
2 3 3

output

4
3
3
1

题目大意与解题思路

给出n点的一棵树,树上每点有权值,求u到v的路径上的第k大
经典题,不过是第一次遇到大概写了1h+,果然不经常用的算法就是容易忘啊,还好自己写板子比较容易理解,还是贴一下代码吧
在树上构造主席树,每个点记录该点到根路径上所有点。u-v的路径上的点就是 u+v-lca(u,v)-father[lca(u,v)]
(因为建树的时候直接从1到n建wa了一发,应该保证父节点已经建过树的)


AC代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;

const LL maxn=1e5+7;

int n,m;///m为hash后的数据范围
int b[maxn];///hash过的数据
int tree[maxn];///tree[i]为第i个版本的根节点
int tot;///用来表示建了多少节点
int val[maxn];
vector <int> edge[maxn];
int fa[maxn];///父
int depth[maxn];///深度,根为1
int vis[maxn];
struct node
{
    int v;///节点值
    int lc,rc;///左右子树
}t[maxn*22];
void init_hash()
{
    for(int i=1;i<=n;i++)
        b[i]=val[i];
    sort(b+1,b+1+n);
    m=unique(b+1,b+1+n)-b-1;
    ///unique可以把相同元素集中到数组前面,返回最后一个数的后一个位置
}
int get_pos(int x)
{
    return lower_bound(b+1,b+1+m,x)-b;
}
int build(int l,int r)
{
    int root=tot++;
    t[root].v=0;
    if(l!=r)
    {
        int mid=(l+r)/2;
        t[root].lc=build(l,mid);
        t[root].rc=build(mid+1,r);
    }
    return root;
}
int update(int root,int l,int r,int x,int v)
{
    ///每次update生成一个新的节点,是从根到需要更新的那个叶子的路径,其他节点与上一个版本完全相同
    int newroot=tot++;
    t[newroot]=t[root];
    t[newroot].v+=v;
    if(l==r)
    {
        return newroot;
    }
    int mid=(l+r)/2;
    if(x>mid)
    {
        t[newroot].rc=update(t[root].rc,mid+1,r,x,v);
    }
    else
    {
        t[newroot].lc=update(t[root].lc,l,mid,x,v);
    }
    return newroot;
}
int query(int l1,int r1,int l2,int r2,int k,int li,int ri)
{
    ///l1,l2,r1,r2分别是多个版本里的相同节点
    ///li和ri只是用来帮助确定节点表示的范围的
    //printf("(%d,%d) %d %d %d\n",li,ri,t[l].v,t[r].v,k);
    if(li==ri) return li;
    int cl1=t[l1].lc,cl2=t[l2].lc,cr1=t[r1].lc,cr2=t[r2].lc;
    if(t[cl1].v+t[cr1].v-t[cl2].v-t[cr2].v>=k)
    {
        return query(cl1,cr1,cl2,cr2,k,li,(li+ri)/2);
    }
    else
    {
        return query(t[l1].rc,t[r1].rc,t[l2].rc,t[r2].rc,k-(t[cl1].v+t[cr1].v-t[cl2].v-t[cr2].v),(li+ri)/2+1,ri);
    }
//    int l1=t[l].lc,l2=t[r].lc;
//    if(t[l2].v-t[l1].v>=k)
//    {
//        return query(l1,l2,k,li,(li+ri)/2);
//    }
//    else
//    {
//        return query(t[l].rc,t[r].rc,k-t[l2].v+t[l1].v,(li+ri)/2+1,ri);
//    }
}
//void dfs(int root,int l,int r)
//{
//    printf("(%d,%d,%d) ",l,r,t[root].v);
//    if(l==r) return;
//    int mid=(l+r)/2;
//    dfs(t[root].lc,l,mid);
//    dfs(t[root].rc,mid+1,r);
//}

void dfs(int x, int d)///初始化为(1,1)
{
    vis[x]=1;
    depth[x]=d;
    d++;
    for(int i=0;i<edge[x].size();i++)
    {
        if(vis[edge[x][i]]==0)
        {
            fa[edge[x][i]]=x;
            dfs(edge[x][i], d);
        }
    }
}
int preLCA[maxn][19];
void pre_lca()
{
    int i,j;
    j=0;
    for(i=1;i<=n;i++)
        preLCA[i][0]=fa[i];
    for(i=1;i<=n;i++)
        for(j=1;j<19;j++)
            preLCA[i][j]=preLCA[preLCA[i][j-1]][j-1];
}
int lca(int u,int v)
{
    int i;
    if(depth[u]<depth[v]) swap(u,v);
    for(i=18;i>=0;i--) if(depth[preLCA[u][i]]>=depth[v]) u=preLCA[u][i];
    for(i=18;i>=0;i--) if(preLCA[u][i]!=preLCA[v][i]) u=preLCA[u][i],v=preLCA[v][i];
    if(u!=v) return fa[u];
    else return u;
}
void dfs2(int x)
{
    vis[x]=1;
    int y=get_pos(val[x]);
    tree[x]=update(tree[fa[x]],1,m,y,1);
    for(int i=0;i<edge[x].size();i++)
    {
        if(vis[edge[x][i]]==0)
        {
            dfs2(edge[x][i]);
        }
    }
}
int main()
{
    int q,T,i,j,k,x,y,l,r,len;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&q);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&val[i]);
            edge[i].clear();
            vis[i]=0;
        }
        for(i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            edge[x].push_back(y);
            edge[y].push_back(x);
        }
        dfs(1,1);
        init_hash();
        tot=0;
        tree[0]=build(1,m);
        for(i=1;i<=n;i++)
        {
            vis[i]=0;
        }
        dfs2(1);
//        for(i=0;i<=n;i++)
//        {
//            dfs(tree[i],1,m);
//            printf("\n");
//        }
        pre_lca();
        for(i=1;i<=q;i++)
        {
            scanf("%d%d%d",&l,&r,&k);
            x=lca(l,r);
            len=depth[l]+depth[r]-depth[x]-depth[fa[x]];
            //printf("len=%d\n",len);
            //printf("x=%d\n",x);
            if(len<k)
            {
                printf("-1\n");
                continue;
            }
            k=len-k+1;
            //printf("k=%d\n",k);
            //printf("(%d,%d,%d,%d)\n",l,r,x,fa[x]);
            x=query(tree[l],tree[r],tree[x],tree[fa[x]],k,1,m);
            //printf("query=%d\n",x);
            printf("%d\n",b[x]);
        }
    }
    return 0;
}

/**
1
3 1
3 2 1
1 2
2 3
2 3 1

2
3 5
3 2 1
1 2
2 3
1 2 1
1 2 2
1 2 3
2 3 1
1 3 3
8 10
1 2 3 4 5 6 7 8
1 2
1 3
2 4
2 5
3 6
3 7
3 8
4 5 2
4 6 3
5 3 2
2 3 3
**/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值