BZOJ 2588: Spoj 10628. Count on a tree 主席树+lca

分析:树上第k小,然后我想说的是主席树并不局限于线性表

        详细分析请看http://www.cnblogs.com/rausen/p/4006116.html,讲的很好,

        然后因为这个熟悉了主席树,真是神器,强制在线,然后顺便学习了LCA倍增算法

       LCA倍增算法是O(nlogn)预处理,然后O(logn)查询,其实和ST是一样的,但是好写啊

    

/**************************************************************
    Problem: 2588
    User: 96655
    Language: C++
    Result: Accepted
    Time:4792 ms
    Memory:47884 kb
****************************************************************/
 
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 1e5+5;
const int INF=0x3f3f3f3f;
typedef unsigned long long ULL;
typedef long long LL;
int a[N],c[N],pos[N],n,q,cnt;
 
int root[N],sz;
struct Node{
  int l,r,v;
}o[N*25];
 
void update(int &rt,int l,int r,int x){
   o[++sz]=o[rt],rt=sz;
   ++o[rt].v;
   if(l==r)return;
   int m=(l+r)>>1;
   if(x<=m)update(o[rt].l,l,m,x);
   else update(o[rt].r,m+1,r,x);
}
 
int d[N],fa[N][20],head[N],tot;
struct Edge{
  int v,next;
}edge[N<<1];
void add(int u,int v){
  edge[tot].v=v;
  edge[tot].next=head[u];
  head[u]=tot++; 
}
void dfs(int u,int f){
   fa[u][0]=f;
   d[u]=d[f]+1;
   update(root[u]=root[f],1,cnt,pos[u]);
   for(int i=head[u];~i;i=edge[i].next){
      int v=edge[i].v;
      if(v==f)continue;
      dfs(v,u);
   }
}
int lca(int u,int v){
  if(d[u]<d[v])swap(u,v);
  for(int t=d[u]-d[v],i=0;t;t>>=1,++i)
   if(t&1)u=fa[u][i];
  if(u==v)return u;
  for(int i=19;i>=0;--i){
    if(fa[u][i]!=-1&&fa[u][i]!=fa[v][i])
      u=fa[u][i],v=fa[v][i]; 
  }
  return fa[u][0];
}
int query(int u,int v,int k){
  int k1=lca(u,v),k2=fa[k1][0];
  u=root[u],v=root[v],k1=root[k1],k2=root[k2];
  int l=1,r=cnt;
  while(l!=r){
    int m=(l+r)>>1;
    int tmp=o[o[u].l].v+o[o[v].l].v-o[o[k1].l].v-o[o[k2].l].v;
    if(k<=tmp)r=m,u=o[u].l,v=o[v].l,k1=o[k1].l,k2=o[k2].l;
    else l=m+1,k-=tmp,u=o[u].r,v=o[v].r,k1=o[k1].r,k2=o[k2].r;
  }
  return a[l];
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i)
      scanf("%d",&a[i]),c[i]=a[i];
    sort(a+1,a+n+1);
    cnt=unique(a+1,a+1+n)-a-1;
    for(int i=1;i<=n;++i)
     pos[i]=lower_bound(a+1,a+1+cnt,c[i])-a;
    tot=root[0]=sz=0;
    memset(head,-1,sizeof(head));
    memset(fa,-1,sizeof(fa));
    for(int i=1;i<n;++i){
      int u,v;
      scanf("%d%d",&u,&v);
      add(u,v),add(v,u);
    }
    dfs(1,0);
    for(int j=1;(1<<j)<=n;++j)
     for(int i=1;i<=n;++i)
      if(fa[i][j-1]!=-1&&i!=1&&j!=0)
       fa[i][j]=fa[fa[i][j-1]][j-1];
    int lastans=0;
    while(q--){
      int u,v,k;
      scanf("%d%d%d",&u,&v,&k);
      u^=lastans;
      printf("%d",lastans=query(u,v,k));
      if(q)printf("\n");
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/shuguangzw/p/5359647.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值