E - Count on a tree 树上第K小

主席树的入门题目,这道题的题意其实就是说,给你一棵树,询问在两个节点之间的路径上的区间第K小

我们如何把树上问题转换为区间问题呢?

其实DFS就可以,我们按照DFS的顺序,对线段树进行建树,那么这个树上问题就可以转换为区间问题了,

那么如何询问来表示两个节点之间的路径呢?

其实也很简单,可以看看以下的图。。。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#define LL long long
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define pb push_back
using namespace std;
const int maxx = 100005;
struct node{
   int l,r,cnt;
}tree[maxx*40];
inline int MID(int l,int r){return (l+r)>>1;};
int root[maxx*40];
int a[maxx];
int ver[maxx*2],Next[maxx*2],head[maxx];
int tot,cnt,n,m;
int t=18;
int fa[maxx],p[maxx][20],deepth[maxx];
vector<int>v;
void add(int x,int y){
   ver[++tot]=y;Next[tot]=head[x];head[x]=tot;
   ver[++tot]=x;Next[tot]=head[y];head[y]=tot;
}
void update(int l,int r,int pre,int &now,int pos){
   now=++cnt;
   tree[now]=tree[pre];
   tree[now].cnt++;
   if (l==r)return;
   int mid=(l+r)>>1;
   if (pos<=mid)
    update(l,mid,tree[pre].l,tree[now].l,pos);
   else
    update(mid+1,r,tree[pre].r,tree[now].r,pos);
}
int query(int l,int r,int L,int R,int k,int lca,int flac){
   if (l==r)return l;
   int tmp=tree[tree[R].l].cnt+tree[tree[L].l].cnt-tree[tree[lca].l].cnt-tree[tree[flac].l].cnt;
   int mid=MID(l,r);
   if (k<=tmp)
     return query(l,mid,tree[L].l,tree[R].l,k,tree[lca].l,tree[flac].l);
   else
     return query(mid+1,r,tree[L].r,tree[R].r,k-tmp,tree[lca].r,tree[flac].r);
}
void dfs(int u,int pre){
   fa[u]=pre;
   deepth[u]=deepth[pre]+1;
   p[u][0]=pre;
   update(1,n,root[pre],root[u],a[u]);
   rep(i,1,18)p[u][i]=p[p[u][i-1]][i-1];
   for (int i=head[u];i;i=Next[i]){
      int y=ver[i];
      if (y==pre)continue;
      dfs(y,u);
   }
}
int LCA(int x,int y){
   if (deepth[x]>deepth[y])swap(x,y);
   per(i,t,0){
     if (deepth[p[y][i]]>=deepth[x])y=p[y][i];
   }
   if (x==y)return y;
   per(i,t,0){
      if(p[x][i]!=p[y][i])x=p[x][i],y=p[y][i];
   }
   return p[x][0];
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        int uu,vv;
        memset(head,0,sizeof(head));
        rep(i,1,n){
          scanf("%d",&a[i]);
          v.pb(a[i]);
        }
        tot=1;
        cnt=0;
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        rep(i,1,n-1){
          scanf("%d%d",&uu,&vv);
          add(uu,vv);
        }
        rep(i,1,n){
          a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
        }
        dfs(1,0);
        int k;
        while(m--){
            scanf("%d%d%d",&uu,&vv,&k);
            int lca=LCA(uu,vv);
            printf("%d\n",v[query(1,n,root[uu],root[vv],k,root[lca],root[fa[lca]])-1]);
        }
    }
   return 0;
}

 

全部评论

相关推荐

11-02 09:49
已编辑
货拉拉_测试(实习员工)
热爱生活的仰泳鲈鱼求你们别卷了:没事楼主,有反转查看图片
点赞 评论 收藏
分享
牛舌:如果我不想去,不管对方给了多少,我一般都会说你们给得太低了。这样他们就会给下一个offer的人更高的薪资了。
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务