[bzoj2588][Spoj 10628] Count on a tree

来自FallDream的博客,未经允许,请勿转载,谢谢。


给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。   n,m<=100000
 
题解:
查的是链,很容易想到树剖。然后我们按照dfs序建一棵主席树,每次询问找到最多log个根同时移动。期望复杂度nlogn(loglogn)
然后去百度了一下,学习了一个新姿势  树上主席树。每个点存一下它到根的路径上的权值,建树的时候直接可持久化,查询的时候直接用两个点的加起来减去lca处的就行了。复杂度是nlogn  
讲道理这样会比树剖快,但是可能是数据原因,跑的没有树剖快qaq
两个的代码都贴一下
 
树剖+主席树
#include<iostream>
#include<cstdio>
#include<algorithm>
#define mp(x,y) make_pair(x,y)
#define MN 100000
#define ll long long
using namespace std;
inline ll read()
{
//    int x;scanf("%lld",&x);return x;
    ll x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

ll last=0;
int n,m,mx[MN+5],size[MN+5],dfn[MN+5],dn=0,cnt=0,dep[MN+5],tot=1;
int a[MN+5],p[MN+5],head[MN+5],top[MN+5],fa[MN+5],rt[MN+5],tp,L[MN+5];
pair<int,int> q[MN+5];
struct node{int l,r,x;}T[MN*30];
struct edge{int to,next;}e[MN*2+5];

void ins(int f,int t)
{
    e[++cnt]=(edge){t,head[f]};head[f]=cnt;
    e[++cnt]=(edge){f,head[t]};head[t]=cnt;
}

void dfs1(int x,int f)
{
    mx[x]=0;size[x]=1;fa[x]=f;    
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=f)
        {
            dep[e[i].to]=dep[x]+1;dfs1(e[i].to,x);
            size[x]+=size[e[i].to];
            if(size[e[i].to]>size[mx[x]]) mx[x]=e[i].to;
        }
}

void dfs2(int x,int tp)
{
    dfn[x]=++dn;p[dn]=x;top[x]=tp;
    if(mx[x]) dfs2(mx[x],tp);
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=fa[x]&&e[i].to!=mx[x])
            dfs2(e[i].to,e[i].to);
}

void ins(int x,int nx,int k)
{
    int l=1,r=tot,mid;
    while(l<r)
    {
        mid=l+r>>1;
        if(k<=mid)
        {
            T[nx].r=T[x].r;T[nx].l=++cnt;
            x=T[x].l;nx=T[nx].l;r=mid;
        }
        else
        {
            T[nx].l=T[x].l;T[nx].r=++cnt;
            x=T[x].r;nx=T[nx].r;l=mid+1; 
        }
        T[nx].x=T[x].x+1;
    }
}

void getrt(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        q[++tp]=mp(rt[dfn[x]],rt[dfn[top[x]]-1]);
        x=fa[top[x]];
    }
    if(dfn[x]>dfn[y]) swap(x,y);
    q[++tp]=mp(rt[dfn[y]],rt[dfn[x]-1]);
}

main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++) L[i]=a[i]=read();
    sort(L+1,L+n+1);
    for(int i=2;i<=n;i++) if(L[i]!=L[i-1]) L[++tot]=L[i];
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read();
        ins(u,v);    
    }
    cnt=0;dfs1(1,0);dfs2(1,1);
    for(int i=1;i<=n;i++) ins(rt[i-1],rt[i]=++cnt,(a[p[i]]=lower_bound(L+1,L+tot+1,a[p[i]])-L));
    for(int i=1;i<=m;i++)
    {
        ll x=read()^last;int y=read(),k=read();tp=0;
        getrt(x,y);
        int l=1,r=tot,mid;
        while(l<r)
        {
            int sum=0;mid=l+r>>1;
            for(int j=1;j<=tp;j++)
                sum+=T[T[q[j].first].l].x-T[T[q[j].second].l].x;
            if(sum<k) 
            {
                k-=sum;l=mid+1;
                for(int j=1;j<=tp;j++)
                    q[j]=mp(T[q[j].first].r,T[q[j].second].r);
            }
            else 
            {
                r=mid;
                for(int j=1;j<=tp;j++)
                    q[j]=mp(T[q[j].first].l,T[q[j].second].l);
            }
        }
        printf("%lld",last=L[l]);if(i!=m) puts("");
    }
    return 0;
}

树上主席树

#include<iostream>
#include<cstdio>
#include<algorithm>
#define MN 100000
#define MK 18
#define ll long long
using namespace std;
inline ll read()
{
    ll x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

ll last=0;
int n,m,cnt=0,dep[MN+5],tot=1,a[MN+5],head[MN+5],fa[MN+5][MK+2],rt[MN+5],L[MN+5];
struct node{int l,r,x;}T[MN*30];
struct edge{int to,next;}e[MN*2+5];

void ins(int f,int t)
{
    e[++cnt]=(edge){t,head[f]};head[f]=cnt;
    e[++cnt]=(edge){f,head[t]};head[t]=cnt;
}

void dfs1(int x,int f)
{
    fa[x][0]=f;    
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=f) dep[e[i].to]=dep[x]+1,dfs1(e[i].to,x);
}

void ins(int x,int nx,int k)
{
    int l=1,r=tot,mid;
    while(l<r)
    {
        mid=l+r>>1;
        if(k<=mid)
        {
            T[nx].r=T[x].r;T[nx].l=++cnt;
            x=T[x].l;nx=T[nx].l;r=mid;
        }
        else
        {
            T[nx].l=T[x].l;T[nx].r=++cnt;
            x=T[x].r;nx=T[nx].r;l=mid+1; 
        }
        T[nx].x=T[x].x+1;
    }
}

void init(int x)
{
    ins(rt[fa[x][0]],rt[x]=++cnt,a[x]=lower_bound(L+1,L+tot+1,a[x])-L);
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=fa[x][0]) init(e[i].to);
}

int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int k=dep[x]-dep[y],j=0;k;k>>=1,++j)
        if(k&1) x=fa[x][j];
    if(x==y) return x;
    for(int i=MK;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++) L[i]=a[i]=read();
    sort(L+1,L+n+1);
    for(int i=2;i<=n;i++) if(L[i]!=L[i-1]) L[++tot]=L[i];
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read();
        ins(u,v);    
    }
    dfs1(1,0);cnt=0;init(1);
    for(int i=1;i<=MK;i++)
        for(int j=1;j<=n;j++)
            fa[j][i]=fa[fa[j][i-1]][i-1];
    for(int i=1;i<=m;i++)
    {
        int x=(int)(read()^last),y=read(),k=read(),b;
        int l=1,r=tot,mid;int z=rt[b=lca(x,y)];x=rt[x];y=rt[y];
        while(l<r)
        {
            mid=l+r>>1;int sum=T[T[x].l].x+T[T[y].l].x-2*T[T[z].l].x+(a[b]>=l&&a[b]<=mid);
            if(sum<k) k-=sum,x=T[x].r,y=T[y].r,z=T[z].r,l=mid+1;
            else x=T[x].l,y=T[y].l,z=T[z].l,r=mid;
        }
        printf("%lld",last=L[l]);if(i!=m) puts("");
    }
    return 0;
}

 

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