SPOJ COT——Count on a tree

题目链接:https://www.spoj.com/problems/COT/

题意:求u到v节点中第k大的数

思路:每次沿着父亲节点建一颗新的树,用lca的树与两节点的树寻找第k大

/*
SPOJ - COT
一棵树,n个节点,m个询问,a1...an表示节点的权,n-1条边,u,v,k询问u和v路径上第k大的数
8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
2 5 2
2 5 3
2 5 4
7 8 2 
ans:
2
8
9
105
7 
思路:每次沿着父亲节点建一颗新的树,用lca的树与两节点的树寻找第k大
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define M 100010
using namespace std;
int n,m,cut,tot,tim,len_disc;
int V[M],head[M],num[2*M],ver[2*M],first[M],root[M],disc[M],far[M];
int dp[2*M][32];
struct edge
{
    int to,nextt;
}E[2*M];
struct segment_tire
{
    int l,r,sum;
    segment_tire(int _l=0,int _r=0,int _sum=0):l(_l),r(_r),sum(_sum){};
}T[20*M];
void init()//初始化
{
    T[0]=segment_tire(0,0,0);
    root[0]=0;
    cut=0,tot=0,tim=0;
    memset(head,-1,sizeof(head));
}
/********邻接表*********/
void add(int from,int to)
{
    E[++cut].to=to;
    E[cut].nextt=head[from];
    head[from]=cut;
}
/********在线LCA*********/
void dfs(int u,int fa,int tep)
{
    first[u]=++tot;
    num[tot]=tep;
    ver[tot]=u;
    for(int i=head[u];i!=-1;i=E[i].nextt)
    {
        int v=E[i].to;
        if(v==fa) continue;
        dfs(v,u,tep+1);
        num[++tot]=tep;
        ver[tot]=u;
    }
}
void ST()
{
    for(int i=1;i<=tot;i++) dp[i][0]=i;
    for(int j=1;(1<<j)<=tot;j++)
    {
        for(int i=1;i+(1<<j)-1<=tot;i++)
        {
            int a=dp[i][j-1],b=dp[i+(1<<j-1)][j-1];
            dp[i][j]=num[a]<num[b]?a:b;
        }
    }
}
int RMQ(int u,int v)
{
    int l=first[u],r=first[v],k=0;
    if(l>r) swap(l,r);
    while(1<<(k+1)<=r-l+1) ++k;
    int a=dp[l][k],b=dp[r-(1<<k)+1][k];
    return num[a]<num[b]?ver[a]:ver[b];
}
/********主席树*********/
void init_disc()
{
    for(int i=1;i<=n;i++) disc[i]=V[i];
    sort(disc+1,disc+1+n);
    len_disc=unique(disc+1,disc+1+n)-disc-1;
}
int discate(int x)
{
    return lower_bound(disc+1,disc+1+n,x)-disc;
}
void update(int &rt,int old,int pos,int l,int r)
{
    T[++tim]=T[old];
    rt=tim;
    T[rt].sum+=1;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(pos<=mid) update(T[rt].l,T[old].l,pos,l,mid);
    else update(T[rt].r,T[old].r,pos,mid+1,r);
}
void build_tire(int u,int fa)
{
    int d=discate(V[u]);
    update(root[u],root[fa],d,1,n);
    far[u]=fa;
    for(int i=head[u];i!=-1;i=E[i].nextt)
    {
        int v=E[i].to;
        if(v==fa) continue;
        build_tire(v,u);
    }
}
int query(int u,int v,int lca,int Flca,int k,int l,int r)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int d=T[T[u].l].sum+T[T[v].l].sum-T[T[lca].l].sum-T[T[Flca].l].sum;
    if(k<=d) return query(T[u].l,T[v].l,T[lca].l,T[Flca].l,k,l,mid);
    else return query(T[u].r,T[v].r,T[lca].r,T[Flca].r,k-d,mid+1,r);
}
int main()
{
    int u,v,k;
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&V[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs(1,-1,1);
    ST();
    init_disc();
    build_tire(1,0);
    while(m--)
    {
        scanf("%d%d%d",&u,&v,&k);
        int lca=RMQ(u,v);
        int ans=query(root[u],root[v],root[lca],root[far[lca]],k,1,n);
        printf("%d\n",disc[ans]);
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值