spoj 10628 Count on a tree (主席树+lca)

题意:给出一棵树,树上每个节点都有权值,有m个询问,求u到v的路径上第k大的数。

思路:用主席树为树上每个节点建立一棵线段树,和区间第k大的求法差不多,由于是在树上,只是添加的顺序有一些问题。每个节点对应的线段树表示从当前节点到根节点的路径中的数据,根据这个,如果我们找到了两个节点的lca,那么我们可以发现,这两条路径其实覆盖了它们公共祖先到根节点的路径,求第k大的时候只要把这一部分减去就行了,不过要注意的是公共祖先这个节点有可能被算了两次,所以查询的时候的区间中有公共祖先,那么要将其减去(在这儿wa了两次。。。)。

 

代码:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
struct Node
{
    int L,R;
    int sum;
}node[maxn*20*2];
struct Edge
{
    int v,next;
}edges[maxn<<1];
struct Data
{
    int w,pos;
    bool operator < (const Data &a)const
    {
        return w<a.w;
    }
}num[maxn];
struct Q
{
    int v,pos,k;
};
vector<Q>querys[maxn];
int node_cnt,n,q,nEdge;
int ans[maxn],d[maxn],head[maxn];
int root[maxn],rank[maxn],f[maxn];
int parents[maxn];
bool vis[maxn];
void AddEdge(int u,int v)
{
    nEdge++;
    edges[nEdge].v=v;
    edges[nEdge].next=head[u];
    head[u]=nEdge;
}
void Init()
{
    for(int i=1;i<=n;++i) querys[i].clear();
    memset(head,0xff,sizeof(head));
    memset(vis,0,sizeof(vis));
    f[1]=0;d[1]=1;
    node_cnt=0;nEdge=-1;
}
int Find(int x)
{
    return x==parents[x]?x:parents[x]=Find(parents[x]);
}
int build(int l,int r)
{
    int x=node_cnt++;
    node[x].sum=0;
    if(l==r) return x;
    int m=(l+r)>>1;
    node[x].L=build(l,m);
    node[x].R=build(m+1,r);
    return x;
}
int Update(int p,int l,int r,int rt)
{
    int x=node_cnt++;
    node[x]=node[rt];
    node[x].sum++;
    if(l==r) return x;
    int m=(l+r)>>1;
    if(p<=m)
      node[x].L=Update(p,l,m,node[x].L);
    else
      node[x].R=Update(p,m+1,r,node[x].R);
    return x;
}
int Query(int r1,int r2,int r3,int l,int r,int k,int s)
{
    if(l==r) return l;
    int m=(l+r)>>1;
    int tmp=node[node[r1].L].sum+node[node[r2].L].sum-node[node[r3].L].sum*2-(s>=l&&s<=m);
    if(tmp>=k)
      return Query(node[r1].L,node[r2].L,node[r3].L,l,m,k,s);
    else
      return Query(node[r1].R,node[r2].R,node[r3].R,m+1,r,k-tmp,s);
}
void tarjan(int u,int fa)
{
    parents[u]=u;
    vis[u]=true;
    root[u]=Update(rank[u],1,n,root[f[u]]);
    int sz=querys[u].size();
    Q tmp;
    for(int i=0;i<sz;++i)
    {
        tmp=querys[u][i];
        if(vis[tmp.v])
        {
            int anc=Find(tmp.v);
            ans[tmp.pos]=Query(root[u],root[tmp.v],root[f[anc]],1,n,tmp.k,rank[anc]);
        }
    }
    for(int h=head[u];h!=-1;h=edges[h].next)
    {
        int v=edges[h].v;
        if(v==fa) continue;
        d[v]=d[u]+1;f[v]=u;
        tarjan(v,u);
        parents[v]=u;
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(~scanf("%d%d",&n,&q))
    {
        Init();
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&num[i].w);
            num[i].pos=i;
        }
        sort(num+1,num+n+1);
        for(int i=1;i<=n;++i)
          rank[num[i].pos]=i;
        int u,v,k;
        for(int i=1;i<n;++i)
        {
            scanf("%d%d",&u,&v);
            AddEdge(u,v);
            AddEdge(v,u);
        }
        root[0]=build(1,n);
        Q tmp;
        for(int i=0;i<q;++i)
        {
            scanf("%d%d%d",&u,&v,&k);
            tmp.v=v;tmp.k=k;tmp.pos=i;
            querys[u].push_back(tmp);
            tmp.v=u;
            querys[v].push_back(tmp);
        }
        tarjan(1,-1);
        for(int i=0;i<q;++i)
          printf("%d\n",num[ans[i]].w);
    }
    return 0;
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值