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: 复制

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

输出样例#1: 复制

2
8
9
105
7

说明

HINT:

N,M<=100000

暴力自重。。。

来源:bzoj2588 Spoj10628.


题解

一道比较涨见识的题目吧。
在这里我们要统计一条路径上的Kth值,考虑把路径转化为一条序列。怎么转换?
我们可以让新增的那个点在原有的fa节点上再建树。这样我们就可以利用树上差分了。
但是我们会发现,x,y两个节点减去两次它们共同的LCA时,LCA的值就没有算在里面了,这个时候我们就不要减去两次LCA应该减去一次LCA,减去一次fa[LCA]。这里的点都是指在主席树相对应的状态。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=100001;
int top[N],size[N],fa[N],dep[N],son[N];
int ch[N],n,m,q,b[N],tr[N<<5],sum[N<<5],tot,l[N<<5],r[N<<5];
int num,head[N],cnt;
struct node{
    int to,next;
}e[N<<1];
int read()
{
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

void add(int from,int to){
    num++;
    e[num].to=to;
    e[num].next=head[from];
    head[from]=num;
}

void  dfs1(int x){
    size[x]=1;
    for(int i=head[x];i;i=e[i].next){
        int v=e[i].to;
        if(!dep[v]){
            dep[v]=dep[x]+1;fa[v]=x;
            dfs1(v);size[x]+=size[v];
            if(son[son[x]]<size[v])son[x]=v;
        }
    }
}

void dfs2(int x,int tp){
    top[x]=tp;if(son[x])dfs2(son[x],tp);
    for(int i=head[x];i;i=e[i].next){
        int v=e[i].to;if(v!=fa[x]&&v!=son[x])dfs2(v,v);
    }
}

int cal(int x,int y){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]<dep[fy])swap(fx,fy),swap(x,y);
        x=fa[fx],fx=top[x];
    }
    if(dep[x]>=dep[y])return y;return x;
}

int build(int left,int right){
    int mid=(left+right)>>1;
    int root=++cnt;
    sum[root]=0;
    if(left<right){
        l[root]=build(left,mid);
        r[root]=build(mid+1,right);
    }
    return root;
}

int update(int pre,int left,int right,int v){
    int mid=(left+right)>>1;
    int root=++cnt;
    l[root]=l[pre];r[root]=r[pre];sum[root]=sum[pre]+1;
    if(left<right){
        if(v<=mid)l[root]=update(l[pre],left,mid,v);
        else r[root]=update(r[pre],mid+1,right,v);
    }
    return root;
}

int query(int u1,int u2,int v1,int v2,int left,int right,int k){
    int mid=(left+right)>>1;
    int x=sum[l[u1]]+sum[l[u2]]-sum[l[v1]]-sum[l[v2]];
    if(left>=right)return left;
    if(x>=k)return query(l[u1],l[u2],l[v1],l[v2],left,mid,k);
    else return query(r[u1],r[u2],r[v1],r[v2],mid+1,right,k-x);
}

void dfs(int x){
    int t=lower_bound(b+1,b+m+1,ch[x])-b;
    tr[x]=update(tr[fa[x]],1,m,t);
    for(int i=head[x];i;i=e[i].next){
        int v=e[i].to;
        if(v!=fa[x])dfs(v);
    }
}

int main()
{
    n=read();q=read();
    for(int i=1;i<=n;i++)
    {
        b[i]=ch[i]=read();
    }
    sort(b+1,b+n+1);
    m=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        add(x,y);add(y,x);
    }
    fa[1]=0;dep[1]=1;
    dfs1(1);dfs2(1,0);
    tr[0]=build(1,m);
    dfs(1);
    int last=0;
    while(q--)
    {
        int l=read(),r=read(),k=read();
        int lca=cal(l^last,r);
        int ff=fa[lca];
        printf("%d\n",last=b[query(tr[l^last],tr[r],tr[lca],tr[ff],1,m,k)]);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hhh1109/p/9245992.html

时间: 2024-10-10 12:30:43

P2633 Count on a tree(主席树)的相关文章

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】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 第一行

【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),表示一组

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

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小的问

[luogu P2633] Count on a tree

[luogu 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),表示一组询问. 输出格式

P2633 Count on a tree(树上主席树)

思路 运用树上差分的思想,转化成一个普通的主席树模型即可求解 代码 #include <cstdio> #include <algorithm> #include <cstring> using namespace std; struct Node{ int lson,rson,sz; }pt[100100*30]; const int MAXlog=19; int dep[100100],jump[100100][MAXlog],lastans=0,n,m,u[100

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复制 8 5 105