SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)

COT - Count on a tree

#tree

You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight.

We will ask you to perform the following operation:

  • u v k : ask for the kth minimum weight on the path from node u to node v

Input

In the first line there are two integers N and M.(N,M<=100000)

In the second line there are N integers.The ith integer denotes the weight of the ith node.

In the next N-1 lines,each line contains two integers u v,which describes an edge (u,v).

In the next M lines,each line contains three integers u v k,which means an operation asking for the kth minimum weight on the path from node u to node v.

Output

For each operation,print its result.

Example

Input:

8 5

8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 82 5 12 5 22 5 32 5 47 8 2 
Output:
2891057 

Submit solution!

题目链接:SPOJ 10628

做了一个晚上无奈网上绝大部分都是用在线RMQ-ST做的,Tarjan离线的少之又少,而且找到的几份Tarjan的也是另外一种方法好像是求深度的,渣渣我不会只会普通的query加边求LCA,那些AC代码真是写的惨不忍睹,变量名字起的根本无法直视也没有基本的缩进,水平不够又看不懂只好拿来跑对拍了。。想想RMQ-ST求LCA又不会,肯定是只能死磕Tarjan了。

先说一下如何建树,一般的主席树是在线性结构上建立的,其实树形结构是线性结构的拓展,任何一条链都是可以看成线性结构,那也就是说原本建树是在arr[i-1]的基础上,在树形结构里显然就是在父亲的基础上建树,显然一个根节点出来的一颗前缀可能被多个儿子继承。因此我是又用一个简单的DFS一路上进行向下建树,其实建树应该是可以合并到Tarjan里去的,只是DFS这样写起来比较清晰。

然后每一次查询就是U->V这一段中的所有点,根据树中LCA的性质有:$cnt_{u-v}=cnt[u]+cnt[v]-2*cnt[LCA(u,v)]$,但是这样可能会存在一些问题,由于前缀的性质prefix[R]-prefix[L]=total[L-1,R],因此这样一来LCA这个点就都被减掉了没有算在里面,但是其实路径上是包含LCA这个点的,那么这个式子需要稍微变形一下,左边的把LCA减掉,右边的把LCA留下,这样刚好凑到一条路且没有点重复,理解起来也比那些判断LCA点值来加减cnt来的简单明了,没有输入外挂1.21s跑完还行…………

即:  $cnt_{u-v}=cnt[u]+cnt[v]-cnt[LCA(u,v)]-cnt[F_LCA(u,v)]$,但是这条式子却不是写起来这么简单,为了这个debug了一个早上,最后发现是传入的F_LCA有问题,试想我们的root是根据不同的左右孩子编号进行识别的,显然跟LCA的点没有关系,但father数组是根据点来映射父亲的,因此不能在递归的时候传原树中LCA的父亲,这样从第二层开始就是错的,应该用LCA的父亲所在的root编号进行传递,这样一来从头到尾用的都是主席树里的节点了…………,最后记得离散化回去,不然答案很奇怪

代码:

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=100010;
struct edge
{
    int to;
    int nxt;
};
struct query
{
    int to;
    int nxt;
    int id;
};
struct info
{
    int l,r,lca,k;
};
struct seg
{
    int lson,rson;
    int cnt;
};
seg T[N*40];
info q[N];
query Q[N<<1];
edge E[N<<1];
int head[N],rhead[N],tot,rtot;
int vis[N],pre[N],ances[N];
int order,arr[N],father[N],root[N],rt_cnt,n;
vector<int>vec;

void init()
{
    CLR(head,-1);
    CLR(rhead,-1);
    tot=0;
    rtot=0;
    CLR(vis,0);
    for (int i=0; i<N; ++i)
    {
        pre[i]=i;
        T[i].cnt=T[i].lson=T[i].rson=0;
    }
    CLR(ances,0);
    CLR(father,0);
    order=0;
    CLR(root,0);
    rt_cnt=0;
    vec.clear();
}
inline void add(int s,int t)
{
    E[tot].to=t;
    E[tot].nxt=head[s];
    head[s]=tot++;
}
inline void addQ(int s,int t,int id)
{
    Q[rtot].id=id;
    Q[rtot].to=t;
    Q[rtot].nxt=rhead[s];
    rhead[s]=rtot++;
}
int Find(int n)
{
    if(pre[n]==n)
        return n;
    return pre[n]=Find(pre[n]);
}
void update(int &cur,int ori,int l,int r,int pos)
{
    cur=++rt_cnt;
    T[cur]=T[ori];
    ++T[cur].cnt;
    if(l==r)
        return ;
    int mid=MID(l,r);
    if(pos<=mid)
        update(T[cur].lson, T[ori].lson, l, mid, pos);
    else
        update(T[cur].rson, T[ori].rson, mid+1, r, pos);
}
int query(int U,int V,int LCA,int F_LCA,int l,int r,int k,int c)
{
    if(l==r)
        return l;
    int mid=MID(l,r);
    int cnt=T[T[U].lson].cnt+T[T[V].lson].cnt-T[T[LCA].lson].cnt-T[T[F_LCA].lson].cnt;
    if(k<=cnt)
        return query(T[U].lson,T[V].lson,T[LCA].lson,T[F_LCA].lson,l,mid,k,c);
    else
        return query(T[U].rson,T[V].rson,T[LCA].rson,T[F_LCA].rson,mid+1,r,k-cnt,c);
}
void Tarjan(int u)
{
    vis[u]=1;
    ances[u]=u;
    int i,v;
    for (i=head[u]; ~i; i = E[i].nxt)
    {
        v = E[i].to;
        if(!vis[v])
        {
            Tarjan(v);
            pre[v]=u;
            ances[Find(u)]=u;
        }
    }
    for (i=rhead[u]; ~i; i = Q[i].nxt)
    {
        v = Q[i].to;
        if(vis[v])
            q[Q[i].id].lca=ances[Find(v)];
    }
}
void dfs_build(int u,int fa)
{
    update(root[u],root[fa],1,n,arr[u]);
    father[u]=fa;
    for (int i=head[u]; ~i; i = E[i].nxt)
    {
        int v=E[i].to;
        if(v!=fa)
            dfs_build(v,u);
    }
}
int main(void)
{
    int m,a,b,i;
    while (~scanf("%d%d",&n,&m))
    {
        init();
        for (i=1; i<=n; ++i)
        {
            scanf("%d",&arr[i]);
            vec.push_back(arr[i]);
        }
        sort(vec.begin(),vec.end());
        vec.erase(unique(vec.begin(),vec.end()),vec.end());
        for (i=1; i<=n; ++i)
            arr[i]=lower_bound(vec.begin(),vec.end(),arr[i])-vec.begin()+1;
        for (i=0; i<n-1; ++i)
        {
            scanf("%d%d",&a,&b);
            add(a,b);
            add(b,a);
        }
        for (i=0; i<m; ++i)
        {
            scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
            q[i].lca=0;
            addQ(q[i].l,q[i].r,i);
            addQ(q[i].r,q[i].l,i);
        }
        Tarjan(1);
        dfs_build(1,0);
        for (i=0; i<m; ++i)
        {
            int indx=query(root[q[i].l],root[q[i].r],root[q[i].lca],root[father[q[i].lca]],1,n,q[i].k,arr[q[i].lca]);
            printf("%d\n",vec[indx-1]);//vec里下标从是0开始的,而我把arr还是设为从1开始,因此要减一
        }
    }
    return 0;
}
时间: 2024-10-07 10:10:02

SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)的相关文章

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

2588: Spoj 10628. Count on a tree 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),表示一组询问.

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

2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数

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

2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MBSubmit: 5217  Solved: 1233[Submit][Status][Discuss] Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一

【BZOJ2588】Spoj 10628. Count on a tree 主席树+LCA

[BZOJ2588]Spoj 10628. Count on a tree 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),表示一组

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

2588: Spoj 10628. Count on a tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2588 Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Inp

bzoj2588: Spoj 10628. Count on a tree(树上第k大)(主席树)

每个节点继承父节点的树,则答案为query(root[x]+root[y]-root[lca(x,y)]-root[fa[lca(x,y)]]) #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> using namespace std; const int maxn=100010; struct poi{int s

Spoj 10628. Count on a tree

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行,表示每个询问的答案.最后一个询问不输出换行符 S

BZOJ2588 Spoj 10628. Count on a tree

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! 题目链接:BZOJ2588 SPOJ10628 正解:主席树上树 解题报告: 考虑主席树上树,每次build都是儿子在父亲的基础上build,然后查询的话就是对于x到y的路径,令$Tx$为$x$对应的线段树,那就是$T_x+T_y-T_{lca}-T_{f

bzoj 2588: Spoj 10628. Count on a tree

DFS序在树上建出主席树,然后..... 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #define ll long long 8 #define M 200009 9 using namespace std; 10 ll read()