【BZOJ2588】Count on a tree,主席树维护链+ST表求LCA

传送门

写在前面:一天下来就写了两道主席树的题……(codevs上的一道智障天梯不算)

思路:

才知道原来主席树不仅可以通过dfs序维护子树区间,还可以直接维护一条到根的链……

我们建好主席树后,每次查询u->v路径上的第k大,无非有两种情况

1.u,v在同一条链上

2.u,v不在同一条链上

其实这两种情况处理起来是一样的,我们利用主席树中的前缀和思路,root[u]并上root[v]再减去root[lca(u,v)]再减去root[fa[lca(u,v)]]就是u->v路径上的点了(可以自己画图感受一下)

所以每次查询时要算四棵树之间的siz关系

注意:

1.求lca我用的是ST表,当然倍增也是可以的

2.最后一个答案不要换行!

代码:

#include<bits/stdc++.h>
#define M 100004
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
using namespace std;
int n,m,lastans,tot,maxn,cnt;
int fa[M],w[M],ID[M],first[M],dep[M],ST[M<<1][20],pos[M],num[M<<1];
struct disc
{
    int data,id;
    bool operator <(const disc other)const
    {
        return data<other.data;
    }
}d[M];
struct edge
{
    int u,v,next;
}e[M<<1];
struct Chairman_tree
{
    int siz,ch[2];
}a[M*20];
int in()
{
    char ch=getchar();int t=0;
    while (!isdigit(ch))ch=getchar();
    while (isdigit(ch)) t=(t<<3)+(t<<1)+ch-48,ch=getchar();
    return t;
}
void add(int x,int y)
{
    e[++tot]=(edge){x,y,first[x]};first[x]=tot;
    e[++tot]=(edge){y,x,first[y]};first[y]=tot;
}
void build(int now,int L,int R,int rt,int val)
{
    a[rt].siz=a[now].siz+1;
    if (L==R) return;
    int mid=(L+R)>>1;
    if (mid>=val)
        rs(rt)=rs(now),
        ls(rt)=++cnt,
        build(ls(now),L,mid,ls(rt),val);
    else
        a[rt].ch[0]=a[now].ch[0],
        a[rt].ch[1]=++cnt,
        build(rs(now),mid+1,R,rs(rt),val);
}
void dfs(int x)
{
    num[++num[0]]=x;
    pos[x]=num[0];
    ST[num[0]][0]=x;
    if (x==1) build(1,1,maxn,2,w[x]);
    for (int i=first[x];i;i=e[i].next)
        if (!pos[e[i].v])
        {
            dep[e[i].v]=dep[x]+1;
            fa[e[i].v]=x;
            build(x+1,1,maxn,e[i].v+1,w[e[i].v]);
            dfs(e[i].v);
            num[++num[0]]=x;
            ST[num[0]][0]=x;
        }
}
void init()
{
    int x,y;
    for (int i=1;i<=log2(num[0]);i++)
        for (int j=1;j<=num[0]-(1<<i)+1;j++)
        x=ST[j][i-1],y=ST[j+(1<<i-1)][i-1],
        ST[j][i]=(dep[x]>dep[y]?y:x);
}
int LCA(int x,int y)
{
    if (pos[x]>pos[y]) swap(x,y);
    int p=pos[x],q=pos[y],t=log2(q-p+1);
    if (dep[ST[p][t]]>dep[ST[q-(1<<t)+1][t]]) return ST[q-(1<<t)+1][t];
    else return ST[p][t];
}
int get(int begin,int end,int u,int v,int lca,int fa,int k)
{
    if (begin==end) return end;
    int mid=(begin+end)>>1;
    int t=a[ls(u)].siz+a[ls(v)].siz-
            a[ls(lca)].siz-a[ls(fa)].siz;
    if (k>t)
        return get(mid+1,end,rs(u),rs(v),rs(lca),rs(fa),k-t);
    else
        return get(begin,mid,ls(u),ls(v),ls(lca),ls(fa),k);
}
main()
{
    n=in();m=in();
    for (int i=1;i<=n;i++)
        w[i]=in(),
        d[i].id=i,d[i].data=w[i];
    sort(d+1,d+n+1);
    maxn=1;
    ID[1]=d[1].data;w[d[1].id]=1;
    for (int i=2;i<=n;i++)
    {
        if (d[i].data!=d[i-1].data) maxn++,ID[maxn]=d[i].data;
        w[d[i].id]=maxn;
    }
    for (int i=1;i<n;i++)
        add(in(),in());
    cnt=n+1;
    dfs(1);
    init();
    int x,y,z,k;
    while (m--)
    {
        x=in()^lastans;
        y=in();k=in();
        z=LCA(x,y);
        lastans=ID[get(1,maxn,x+1,y+1,z+1,fa[z]+1,k)];
        printf("%d",lastans);
        if (m) putchar(‘\n‘);
    }
}
时间: 2024-08-29 18:02:19

【BZOJ2588】Count on a tree,主席树维护链+ST表求LCA的相关文章

【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】Count on a tree 主席树 + 倍增

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

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),表示一组询问.

spoj cot: Count on a tree 主席树

10628. Count on a tree Problem code: COT 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

BZOJ 2588 Count on a tree 主席树+倍增LCA

题目大意:给定一棵树,每个节点有权值,询问两个节点路径上的权值第k小 这题很卡时间... 树链剖分+二分+树套树的O(nlog^4n)做法可以去死了 没有修改操作,树链剖分+二分+划分树O(nlog^3n),还是死了 我怒了,裸学了一发可持久化线段树(不看任何代码OTZ,我是怎么做到的0.0),二分+主席树,O(nlog^2n),居然还是死了! 最后发现我SB了,完全没有必要二分,直接把4个参全传下去就行了,O(nlogn) 首先我们对于每个节点维护这个节点到根的权值线段树 然后对于每个询问(x

P2633 Count on a tree(主席树)

题目描述 给定一棵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行,表示每个询问的答案. 输入输出样例 输入样例#1:

spoj COT - Count on a tree(主席树 +lca,树上第K大)

您将获得一个包含N个节点的树.树节点的编号从1到?.每个节点都有一个整数权重. 我们会要求您执行以下操作: uvk:询问从节点u到节点v的路径上的第k个最小权重 输入 在第一行中有两个整数?和中号.(N,M <= 100000) 在第二行中有N个整数.第i个整数表示第i个节点的权重. 在接下来的N-1行中,每行包含两个整数u v,它描述了一个边(u,v). 在接下来的M行中,每行包含三个整数u v k,这意味着要求从节点u到节点v的路径上的第k个最小权重的操作. 解题思路: 首先对于求第K小的问

HDU 3333 Turing Tree (主席树)

题意:给定上一个序列,然后有一些询问,求区间 l - r 中有多少个不同的数的和. 析:和求区间不同数的方法是一样,只要用主席树维护就好. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <iost

spoj COT/bzoj2588 Count on a tree

这题是学主席树的时候就想写的,,, 但是当时没写(懒) 现在来填坑 = =日常调半天lca(考虑以后背板) 主席树还是蛮好写的,但是代码出现重复,不太好,导致调试的时候心里没底(虽然事实证明主席树部分没出问题) 1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 struct lis{ int x,id;} li[100001]; 5 struct nod{ int num,ls,rs;} t[80