Count on a tree SPOJ 主席树+LCA(树链剖分实现)(两种存图方式)

Posted alking1001

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Count on a tree SPOJ 主席树+LCA(树链剖分实现)(两种存图方式)相关的知识,希望对你有一定的参考价值。

Count on a tree SPOJ 主席树+LCA(树链剖分实现)(两种存图方式)

题外话,这是我第40篇随笔,纪念一下。<( ̄︶ ̄)↗[GO!]

题意

是说有棵树,每个节点上都有一个值,然后让你求从一个节点到另一个节点的最短路上第k小的值是多少。

解题思路

看到这个题一想以为是树链剖分+主席树,后来写着写着发现不对,因为树链剖分我们分成了一小段一小段,这些小段不能合并起来求第k小,所以这个想法不对。奈何不会做,查了查题解,需要用LCA(最近公共祖先),然后根据主席树具有区间加减的性质,我们查询一段区间的状态可以从LCA的角度去看问题,找到LCA(x, y)然后,我们只要一个LCA节点,然后求出区间X到根节点,以及Y到根节点的关系式来推这个关系,但是千万不要去减两倍LCA的关系,因为那样就会少掉一个节点了,于是,就dfs()往下建树,就是寻找到最后的答案。
\\[ t[t[x].l].sum + t[t[y].l].sum - t[t[lca].l].sum - t[t[gra].l].sum \\]
注意:这里我求LCA的方法是用的树链剖分的方法,求LCA的方法有很多,但是我就会这一种??

如果没有学过树链剖分,我这里有一些学习资料推荐,点我

如果没有学过主席树,别急,我这也有好的视频和博客推荐,点我

上面两个都是我学习过程中遇到的好的博客文章的收集,节省再次查找的时间

代码实现(图用vector存版,用链式向前星版)

//vector版,方便但是稍微慢一些
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1e5+100;
struct node
    int l, r, sum;
t[maxn*40];
vector<int> g[maxn];
vector<int> v;
int tot, root[maxn], w[maxn];

int dep[maxn], f[maxn], size[maxn], son[maxn]; 
int top[maxn];
int n, m, nn; //nn是实际去重后的个数
int read() //快读函数,这里已经实验过了,用和不用都行,用了会快一些。

    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9')if(ss=='-')f=-1;ss=getchar();
    while(ss>='0'&&ss<='9')x=x*10+ss-'0';ss=getchar();
    return f*x;

int getid(int x) //离散化之后求坐标

    return lower_bound(v.begin() , v.end() , x)- v.begin() +1;

void dfs1(int u, int fa, int depth)

    size[u]=1;
    dep[u]=depth;
    f[u]=fa;
    int len=g[u].size();
    for(int i=0; i<len; i++)
    
        int v=g[u][i];
        if(v==fa) 
            continue;
        dfs1(v, u, depth+1);
        size[u]+=size[v];
        if(size[v] > size[son[u]])
            son[u]=v;
    

void dfs2(int u, int tp)

    top[u]=tp;
    if(!son[u])
        return ;
    dfs2(son[u], tp);
    int len=g[u].size();
    for(int i=0; i<len; i++)
    
        int v=g[u][i];
        if(v==son[u] || v==f[u])
            continue;
        dfs2(v, v);
    

int lca(int x,int y)

    int fx=top[x], fy=top[y];
    while(fx!=fy)
    
        if(dep[fx] < dep[fy]) 
        
            swap(x, y);
            swap(fx, fy);
        
        x=f[fx]; //这里右边是fx,千万别写错了,我就是这犯了错,wa了几十下。。。
        fx=top[x];
    
    return dep[x] < dep[y] ? x : y ;

void update(int l, int r, int pre, int &now, int pos)

    t[++tot]=t[pre];
    t[tot].sum++;
    now=tot;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(pos<=mid)
        update(l, mid, t[pre].l, t[now].l, pos);
    else 
        update(mid+1, r, t[pre].r, t[now].r, pos);

int query(int l, int r, int x, int y, int lca, int gra, int k)

    if(l==r)
        return l;
    int mid=(l+r)>>1;
    int sum=t[t[x].l].sum + t[t[y].l].sum - t[t[lca].l].sum - t[t[gra].l].sum ;
    if(k<=sum)
        return query(l, mid, t[x].l, t[y].l, t[lca].l, t[gra].l, k);
    else 
        return query(mid+1, r, t[x].r, t[y].r, t[lca].r, t[gra].r, k-sum); 

void dfs(int u)

    int pos=getid(w[u]);
    update(1, nn, root[f[u]], root[u], pos);
    int len=g[u].size();
    for(int i=0; i<len; i++)
    
        int v=g[u][i];
        if(v==f[u])
            continue;
        dfs(v);
    

int main()

    scanf("%d%d", &n, &m); 
    for(int i=1; i<=n; i++)
    
        w[i]=read();
        v.push_back(w[i]);
    
    sort(v.begin() , v.end() );
    v.erase( unique( v.begin() , v.end() ), v.end());
    nn=v.size();
    int x, y;
    for(int i=1; i<n; i++)
    
        scanf("%d%d", &x, &y);
        g[x].push_back(y);
        g[y].push_back(x);
    
    dfs1(1, 0, 1);
    dfs2(1, 1);
    dfs(1);
    int k, la;
    for(int i=1; i<=m; i++)
    
        scanf("%d%d%d", &x, &y, &k);
        la=lca(x, y);
        printf("%d\\n", v[ query(1, nn, root[x], root[y], root[la], root[ f[la] ], k) -1 ] );
    
    return 0;
// 链式向前星版存图
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
const int maxn=1e5+10000;
struct node
    int l, r, sum;
t[maxn*40];
int tot; //tot是主席树点的个数 

struct edge
    int to, next;
e[maxn<<1];
int head[maxn], cnt; //cnt是边的个数 

int root[maxn], w[maxn], id[maxn];

int dep[maxn], f[maxn], size[maxn], son[maxn];
int top[maxn];
int n, m, nn;
inline int read()

    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9')if(ss=='-')f=-1;ss=getchar();
    while(ss>='0'&&ss<='9')x=x*10+ss-'0';ss=getchar();
    return f*x;

inline void add(int u, int v)

    e[++cnt].next=head[u];
    e[cnt].to=v;
    head[u]=cnt;


int getid(int x)

    return (lower_bound(&id[1] , &id[nn+1] , x)-id);


void update(int l, int r, int pre, int &now, int pos)

    t[++tot]=t[pre];
    t[tot].sum++;
    now=tot;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(pos<=mid)
        update(l, mid, t[pre].l, t[now].l, pos);
    else
        update(mid+1, r, t[pre].r, t[now].r, pos);


int query(int l, int r, int x, int y, int lca, int gra, int k)

    if(l==r)
        return l;
    int mid=(l+r)>>1;
    int sum=t[t[x].l].sum + t[t[y].l].sum - t[t[lca].l].sum - t[t[gra].l].sum ;
    if(k<=sum)
        return query(l, mid, t[x].l, t[y].l, t[lca].l, t[gra].l, k);
    else
        return query(mid+1, r, t[x].r, t[y].r, t[lca].r, t[gra].r, k-sum);

void dfs1(int u, int fa, int depth)

    size[u]=1;
    dep[u]=depth;
    f[u]=fa;
    for(int i=head[u]; i; i=e[i].next)
    
        int v=e[i].to;
        if(v==fa)
            continue;
        dfs1(v, u, depth+1);
        size[u]+=size[v];
        if(size[v] > size[son[u]])
            son[u]=v;
    

void dfs2(int u, int tp)

    top[u]=tp;
    if(!son[u])
        return ;
    dfs2(son[u], tp);
    for(int i=head[u]; i; i=e[i].next)
    
        int v=e[i].to;
        if(v==son[u] || v==f[u])
            continue;
        dfs2(v, v);
    


int lca(int x,int y)

    int fx=top[x], fy=top[y];
    while(fx!=fy)
    
        if(dep[fx] < dep[fy])
        
            swap(x, y);
            swap(fx, fy);
        
        x=f[fx];
        fx=top[x];
    
    return dep[x] < dep[y] ? x : y ;

void dfs(int u)

    int pos=getid(w[u]);
    update(1, nn, root[f[u]], root[u], pos);
    for(int i=head[u]; i; i=e[i].next)
    
        int v=e[i].to;
        if(v==f[u])
            continue;
        dfs(v);
    

int main()

    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++)
    
        scanf("%d", &w[i]);
        id[i]=w[i];
    
    sort(&id[1], &id[n+1] );
    nn = (unique( &id[1], &id[n+1])-id-1) ;
    int x, y;
    for(int i=1; i<n; i++)
    
        scanf("%d%d", &x, &y);
        add(x, y);
        add(y, x);
    
    dfs1(1, 0, 1);
    dfs2(1, 1);
    dfs(1);
    int k, la;
    for(int i=1; i<=m; i++)
    
        scanf("%d%d%d", &x, &y, &k);
        la=lca(x, y);
        printf("%d\\n", id[ query(1, nn, root[x], root[y], root[la], root[ f[la] ], k) ] );
    
    return 0;

以上是关于Count on a tree SPOJ 主席树+LCA(树链剖分实现)(两种存图方式)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]

[SPOJ10628]Count on a tree(主席树)

SPOJ Count on a tree 主席树+lca

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

spoj COT - Count on a tree (树上第K小 LCA+主席树)

bzoj 2588: Spoj 10628. Count on a tree LCA+主席树