1. 程式人生 > >BZOJ2588Count on a tree——LCA+主席樹

BZOJ2588Count on a tree——LCA+主席樹

div map 相加 sam cstring 換行符 cpp swap \n

題目描述

給定一棵N個節點的樹,每個點有一個權值,對於M個詢問(u,v,k),你需要回答u xor lastans和v這兩個節點間第K小的點權。其中lastans是上一個詢問的答案,初始為0,即第一個詢問的u是明文。

輸入

第一行兩個整數N,M。 第二行有N個整數,其中第i個整數表示點i的權值。 後面N-1行每行兩個整數(x,y),表示點x到點y有一條邊。 最後M行每行兩個整數(u,v,k),表示一組詢問。

輸出

M行,表示每個詢問的答案。最後一個詢問不輸出換行符

樣例輸入

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

樣例輸出

2
8
9
105
7

提示

HINT: N,M<=100000      查詢一個路徑上第k小,就相當於查詢序列上區間第k小,可以想到用主席樹維護。但這道題是在樹上完成的操作,所以並不能像平常主席樹一樣只用r時刻減掉l-1時刻的線段樹查詢。對於樹上的路徑(以x和y之間的路徑為例),可以把它分成兩部分:x到lca和y到lca。這樣整條路徑上的信息就可以通過這兩條鏈相加得到。所以直接用x時刻線段樹+y時刻線段樹-lca時刻線段樹-lca的父節點時刻線段樹就得到路徑上狀態。每個時刻線段樹由它的父節點時刻線段樹轉移過來。
最後附上代碼。
#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mid (L+R)/2
using namespace std;
int ans;
int tot;
int cnt;
int anc;
int n,m,q;
int x,y,z;
int d[100010];
int v[100010];
int h[100010];
int l[3000010];
int r[3000010];
int to[200010];
int head[100010];
int next[200010];
int sum[3000010];
int root[100010];
int f[100010][20];
map<int,int>b;
void add(int x,int y)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
int lca(int x,int y)
{
    if(d[x]<d[y])
    {
        swap(x,y);
    }
    int dep=d[x]-d[y];
    for(int i=0;i<=19;i++)
    {
        if((dep&(1<<i))!=0)
        {
            x=f[x][i];
        }
    }
    if(x==y)
    {
        return x;
    }
    for(int i=19;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
int updata(int pre,int L,int R,int k)
{
    int rt=++cnt;
    l[rt]=l[pre];
    r[rt]=r[pre];
    sum[rt]=sum[pre]+1;
    if(L==R)
    {
        return rt;
    }
    else
    {
        if(k<=mid)
        {
            l[rt]=updata(l[pre],L,mid,k);
        }
        else
        {
            r[rt]=updata(r[pre],mid+1,R,k);
        }
    }
    return rt;
}
int query(int x,int y,int anc,int fa,int L,int R,int k)
{
    if(L==R)
    {
        return b[L];
    }
    int num=sum[l[x]]+sum[l[y]]-sum[l[anc]]-sum[l[fa]];
    if(num>=k)
    {
        return query(l[x],l[y],l[anc],l[fa],L,mid,k);
    }
    else
    {
        return query(r[x],r[y],r[anc],r[fa],mid+1,R,k-num);
    }
}
void dfs(int x,int fa)
{
    d[x]=d[fa]+1;
    int k=lower_bound(h+1,h+1+m,v[x])-h;
    b[k]=v[x];
    root[x]=updata(root[fa],1,n,k);
    for(int i=1;i<=19;i++)
    {
        f[x][i]=f[f[x][i-1]][i-1];
    }
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=fa)
        {
            f[to[i]][0]=x;
            dfs(to[i],x);
        }
    }
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&v[i]);
        h[i]=v[i];
    }
    sort(h+1,h+1+n);
    m=unique(h+1,h+1+n)-h-1;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        x=x^ans;
        anc=lca(x,y);
        ans=query(root[x],root[y],root[anc],root[f[anc][0]],1,n,z);
        printf("%d\n",ans);
    }
}

BZOJ2588Count on a tree——LCA+主席樹