Count on a tree SPOJ - COT 主席树+lca

题目链接

样例解释:

这里写图片描述

给出n和m表示有n个点和m个询问
下一行有n个值代表对应点的权值
然后是n-1行的u和v,表示u和v相连
最后是m个询问,每个询问有u,v,k,表示u到v这条链上第k大的权值。
如询问 2 5 1 就是图中画出来的这条链中第一个大的权值,那就是2了。

解法:
这道题目跟主席树入门题目:求一段区间第k大很像,不同的是这道题目是求在树的一条链上的第k大。一开始的时候我只感觉到这道题目隐约的跟lca有点关系,但是不知道该如何去处理。然后我看了别人的博客,但是大多数都是直接给出了一个结论:对于查询区间[u,v],答案就是root[u]+root[v]-root[lca]-root[lca的父亲]上的第k大
一开始不是很理解,后来发现其实类似于前缀和,对于一个节点,它的每个儿子节点做一棵新版本的树。如果从整棵树的根节点开始做的话,最后得到的一个root[u],就表示了u这个节点到根节点的信息。如果要获得从u到v这条链上的信息,拿就要先算出这两个点的lca,u到lca的信息是通过root[u]-root[lca的父亲]获得的,那么另外的一段就是root[v]-root[lca]获得的。合起来就是那个结论了。
如果不是很理解的话,就举上面图中的例子,节点1是2和5的lca。2到5这条链的信息由2——1和5——3组成。类比一下前缀和,对一个区间[l,r]的询问,答案就是sum[r]-sum[l-1],所以这里2到1的信息就是root[2]-root[1的父亲],实际上整棵树有一个虚根0,图中没有画出来,节点1的父亲就是0节点。然后5到3这边就是root[5]-root[3的父亲(也就是1)].然后两端加起来就是整段的信息了,在整段信息中查询第k大就是答案了。
和普通区间求第k大一样,这道题目的权值是离散的,需要离散化一下。

这里写图片描述

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

const int maxn=200005;
const int tp=18;
int n,m;
int tot,cnt,sz;
int root[maxn],lson[maxn*20],rson[maxn*20],tsize[maxn*20];
int head[maxn];
int num[maxn],ls[maxn];
int fa[maxn],dep[maxn];
int f[maxn][tp];

void read(int &m)
{
    char ch;
    int flag = 0;
    while ((ch = getchar()) < '0' || ch > '9')
    {
        if (ch == '-')flag = 1;
    }
    for (m = 0; ch >= '0' && ch <= '9'; ch = getchar())
        m = m * 10 + ch - '0';
    if (flag)
        m *= -1;
}

struct node
{
    int v,nxt;
}edge[maxn*4];

void addedge(int u,int v)
{
    cnt++;
    edge[cnt].v=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
}

void updata(int last,int cur,int l,int r,int k)
{


    tsize[cur]=tsize[last]+1;
    lson[cur]=lson[last];
    rson[cur]=rson[last];
    if(l==r)return;
    int mid=(l+r)>>1;
    if(k<=mid)updata(lson[last],lson[cur]=++tot,l,mid,k);
    else updata(rson[last],rson[cur]=++tot,mid+1,r,k);
}

int query(int last,int cur,int lcart,int lcafa,int l,int r,int k)
{
    if(l==r)return l;
    int sum=tsize[lson[last]]+tsize[lson[cur]]-tsize[lson[lcart]]-tsize[lson[lcafa]];
    int mid=(l+r)>>1;
    if(k<=sum)return query(lson[last],lson[cur],lson[lcart],lson[lcafa],l,mid,k);
    else return query(rson[last],rson[cur],rson[lcart],rson[lcafa],mid+1,r,k-sum);
}

int get_ls(int x)
{
    return lower_bound(ls+1,ls+sz+1,x)-ls;
}

void dfs(int now,int fat)
{
    fa[now]=fat;
    dep[now]=dep[fat]+1;
    f[now][0]=fat;
    for(int i=1;i<tp;i++)f[now][i]=f[f[now][i-1]][i-1];
    updata(root[fat],root[now]=++tot,1,sz,get_ls(num[now]));
    for(int i=head[now];~i;i=edge[i].nxt)
    {
        int v=edge[i].v;
        if(v==fat)continue;
        dfs(v,now);
    }
}

int lca(int a,int b)
{
    if(dep[a]>dep[b])swap(a,b);
    if(dep[a]<dep[b])
    {
        int del=dep[b]-dep[a];
        for(int i=0;i<tp;i++)
            if(del&(1<<i))b=f[b][i];
    }
    if(a==b)return a;
    for(int i=tp-1;i>=0;i--)
    {
        if(f[a][i]!=f[b][i])
        {
            a=f[a][i],b=f[b][i];
        }
    }
    return f[a][0];
}

int main()
{
//    freopen("data.in","r",stdin);
    read(n),read(m);
    memset(head,-1,sizeof(head));
    tot=cnt=0;
    for(int i=1;i<=n;i++)
        read(num[i]),ls[i]=num[i];
    sort(ls+1,ls+1+n);
    sz=unique(ls+1,ls+1+n)-ls-1;
    int x,y,z;
    for(int i=1;i<n;i++)
    {
        read(x),read(y);
        addedge(x,y);
        addedge(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=m;i++)
    {
        read(x),read(y),read(z);
        int comfa=lca(x,y);
        printf("%d\n",ls[query(root[x],root[y],root[comfa],root[fa[comfa]],1,sz,z)]);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值