[bzoj2588][count on a tree] (主席树+lca)

Description

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

Input

第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。

Output

M行,表示每个询问的答案。

Sample Input

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
0 5 2
10 5 3
11 5 4
110 8 2

Sample Output

2
8
9
105
7 

HINT

N,M<=100000

暴力自重。。。

Source

鸣谢seter

Solution

普通的树上主席树

 先上我的TLE树链剖分+主席树

#include<map>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100010
#define buf 1<<21
using namespace std;
char B[buf],*p=B;
inline int Rin(){
    int x=0,f=1;
    for(;*p<'0'||*p>'9';p++)
        if(*p=='-')f=-1;
    for(;*p>='0'&&*p<='9';p++)
        x=(x<<1)+(x<<3)+*p-'0';
    return x*f;
}
bool vis[N];
vector<int>d;
map<int,int>h;
int ind,n,q,ans,val[N],sz[N],rk[N],id[N],mx[N],fa[N],dep[N],top[N];
struct pt{
    int v;pt *nt;
}*fst[N],mem[N<<1],*e=mem;
inline void link(int x,int y){
    *++e=(pt){y,fst[x]},fst[x]=e;
    *++e=(pt){x,fst[y]},fst[y]=e;
}
void dfs1(int x){
    rk[++ind]=x,
    id[x]=ind,
    vis[x]=sz[x]=1;
    for(pt *j=fst[x];j;j=j->nt)
        if(!vis[j->v])
            fa[j->v]=x,
            dep[j->v]=dep[x]+1,
            dfs1(j->v),sz[x]+=sz[j->v],
            mx[x]=sz[mx[x]]<sz[j->v]?j->v:mx[x];
}
void dfs2(int x){
    vis[x]=0;
    top[x]=x^mx[fa[x]]?x:top[fa[x]];
    if(mx[x]){
        dfs2(mx[x]);
        for(pt *j=fst[x];j;j=j->nt)
            if(vis[j->v])dfs2(j->v);
    }
}
inline int lca(int x,int y){
    while(top[x]^top[y])
        dep[top[x]]>dep[top[y]]?
        x=fa[top[x]]:
        y=fa[top[y]];
    return dep[x]<dep[y]?x:y;
}
struct Seg{
    Seg *l,*r;int s;
    Seg(){}
    Seg(Seg *_l,Seg *_r,int _s)
    :l(_l),r(_r),s(_s){}
}*rt[N],*nil=new Seg(0x0,0x0,0);
Seg *build(Seg *p,int s,int t,int k){
    if(!(s^t))return new Seg(rt[0],rt[0],p->s+1);
    int mid=s+t>>1;
    return k<=mid?
           new Seg(build(p->l,s,mid,k),p->r,p->s+1):
           new Seg(p->l,build(p->r,mid+1,t,k),p->s+1);
}
int secret(Seg *p1,Seg *p2,Seg *p3,Seg *p4,int s,int t,int k){
    while(s<t){
        int mid=s+t>>1;
        int c=p1->l->s+p2->l->s-p3->l->s-p4->l->s;
        if(k<=c)
            t=mid,
            p1=p1->l,
            p2=p2->l,
            p3=p3->l,
            p4=p4->l;
        else
            k-=c,
            s=mid+1,
            p1=p1->r,
            p2=p2->r,
            p3=p3->r,
            p4=p4->r;
    }
    return d[s-1];
}
inline int feel(int x,int y,int k){
    int t=lca(x,y);
    return secret(rt[id[x]],rt[id[y]],rt[id[t]],rt[id[fa[t]]],1,d.size(),k);
}
int main(){
    fread(B,1,buf,stdin);
    n=Rin(),q=Rin();
    for(int i=1;i<=n;i++)
        val[i]=Rin(),d.push_back(val[i]);
    sort(d.begin(),d.end());
    d.erase(unique(d.begin(),d.end()),d.end());
    for(int i=0;i<d.size();i++)
        h[d[i]]=i+1;
    for(int i=1;i<=n;i++)
        val[i]=h[val[i]];
    for(int i=1;i<n;i++){
        int x=Rin(),y=Rin();
        link(x,y);
    }
    dfs1(1);dfs2(1);
    rt[0]=nil;
    rt[0]->l=rt[0]->r=rt[0];
    for(int i=1;i<=n;i++)
        rt[i]=build(rt[id[fa[rk[i]]]],1,d.size(),val[rk[i]]);
    while(q--){
        int x=Rin(),y=Rin(),k=Rin();
        printf("%d\n",ans=feel(x^ans,y,k));
    }
    return 0;
}

ac倍增+主席树

 注意root的深度不能设为0

#include<map>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100100;
inline int Rin(){
    int x=0,c=getchar(),f=1;
    for(;c<48||c>57;c=getchar())
        if(!(c^45))
            f=-1;
    for(;c>47&&c<58;c=getchar())
        x=(x<<1)+(x<<3)+c-48;
    return x*f;
}
bool vis[N];
pair<int,int>v[N];
int n,m,ecnt,ans,fst[N],q[N],a[N*32],dep[N],fa[N][22],bin[22],rt[N],tot,sum[N*32],ls[N*32],rs[N*32];
void build(int &pr,int pl,int s,int t,int k){
    pr=++tot;
    sum[pr]=sum[pl]+1;
    if(!(s^t))return;
    ls[pr]=ls[pl];
    rs[pr]=rs[pl];
    int mid=s+t>>1;
    if(k<=mid)build(ls[pr],ls[pl],s,mid,k);
    else build(rs[pr],rs[pl],mid+1,t,k);
}
struct edge{
    int v,nxt;
}e[N<<1];
inline void link(int x,int y){
    e[++ecnt].v=y;
    e[ecnt].nxt=fst[x];
    fst[x]=ecnt;
}
void bfs(){
    int hd=0,tl=1;
    vis[1]=1;q[0]=1;
    while(hd^tl){
        int now=q[hd++];
        build(rt[now],rt[fa[now][0]],1,n,a[now]);
        for(int j=1;j<=19;j++)
            if(bin[j]<=dep[now])
                fa[now][j]=fa[fa[now][j-1]][j-1];
            else
                break;
        for(int j=fst[now];j;j=e[j].nxt)
            if(!vis[e[j].v])
                vis[e[j].v]=1,
                fa[e[j].v][0]=now,
                dep[e[j].v]=dep[now]+1,
                q[tl++]=e[j].v;
    }
}
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int j=19;~j;j--)
        if(dep[fa[x][j]]>=dep[y])
            x=fa[x][j];
    if(!(x^y))return x;
    for(int j=19;~j;j--)
        if(fa[x][j]^fa[y][j])
            x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}
int query(int p1,int p2,int p3,int p4,int s,int t,int k){
    if(!(s^t))return v[t].first;
    int mid=s+t>>1,c=sum[ls[p1]]+sum[ls[p2]]-sum[ls[p3]]-sum[ls[p4]];
    if(k<=c)return query(ls[p1],ls[p2],ls[p3],ls[p4],s,mid,k);
    return query(rs[p1],rs[p2],rs[p3],rs[p4],mid+1,t,k-c);
}
int req(int x,int y,int k){
    int t=lca(x,y);
    return query(rt[x],rt[y],rt[t],rt[fa[t][0]],1,n,k);
}
int main(){
    for(int i=0;i<=19;i++)
        bin[i]=1<<i;
    n=Rin(),m=Rin();
    for(int i=1;i<=n;i++)
        v[i].first=Rin(),v[i].second=i;
    sort(v+1,v+1+n);
    for(int i=1;i<=n;i++)
        a[v[i].second]=i;
    for(int i=1;i<n;i++){
        int x=Rin(),y=Rin();
        link(x,y);link(y,x);
    }
    dep[1]=1;bfs();
    for(int i=1;i<=m;i++)  
    {
        int x,y,k;
        scanf("%d%d%d",&x,&y,&k);
        printf("%d",ans=req(x^ans,y,k));
        if(i!=m)puts("");
    }
    return 0;
}

 可持久化线段树注意事项

1.对于较大的数据范围,用动态开点线段树时注意直接l+r>>1会爆,可以用l+r>>1=(l>>1)+(r>>1)+(l&r&1)

2.对于lca问题,根节点的深度统一设为1

3.树剖是O(2n)的,可能会超时,用倍增+宽搜可能稍稍快一些

4.我认为指针这种东西最好少用,处理不好可能RE

posted @ 2017-01-04 11:22  keshuqi  阅读(208)  评论(0编辑  收藏  举报