【BZOJ-2588】Count on a tree 主席树 + 倍增

2588: Spoj 10628. Count on a tree

Time Limit: 12 Sec  Memory Limit: 128 MB
Submit: 3749  Solved: 873
[Submit][Status][Discuss]

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行,表示每个询问的答案。

Sample Input

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

Sample Output

2
8
9
105
7

HINT

HINT:

N,M<=100000

暴力自重。。。

Source

鸣谢seter

Solution

区间第k小?必然是主席树

树上第k小?显然把主席树建在树上即可,亦或把主席树建在DFS序上

那么考虑把主席树建在树上,仍旧需要DFS序,还需要预处理出LCA,对建树和询问做一些修改即可

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int read()
{
    int 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;
}
#define maxn 100010
int n,m;int val[maxn];int ls[maxn],num;
struct data{int to,next;}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){cnt++;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt;}
void insert(int u,int v){add(u,v);add(v,u);}
int steck[maxn],top,father[maxn][25],deep[maxn],pos[maxn];
void dfs(int x)
{
    steck[++top]=x; pos[x]=top;
    for (int i=1; i<=20; i++)
        if (deep[x]>=(1<<i))
            father[x][i]=father[father[x][i-1]][i-1];
        else break;
    for (int i=head[x]; i; i=edge[i].next)
        if (father[x][0]!=edge[i].to)
            father[edge[i].to][0]=x,deep[edge[i].to]=deep[x]+1,dfs(edge[i].to);
}
int LCA(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int dd=deep[x]-deep[y];
    for (int i=0; i<=20; i++)
        if (dd&(1<<i)) x=father[x][i];
    for (int i=20; i>=0; i--)
        if (father[x][i]!=father[y][i])
            x=father[x][i],y=father[y][i];
    if (x==y) return x; else return father[x][0];
}
int sum[maxn*20],ll[maxn*20],rr[maxn*20],root[maxn<<2],sz;
void update(int l,int r,int &now,int fat,int va)
{
    now=++sz; sum[now]=sum[fat]+1;
    if (l==r) return;
    ll[now]=ll[fat],rr[now]=rr[fat];
    int mid=(l+r)>>1;
    if (va<=mid) update(l,mid,ll[now],ll[fat],va);
    else update(mid+1,r,rr[now],rr[fat],va);
}
int query(int l,int r,int L,int R,int k)
{
    int lca=LCA(L,R),fa=father[lca][0];
    int a=root[pos[L]],b=root[pos[R]],c=root[pos[lca]],d=root[pos[fa]];
    while (l<r)
        {
            int mid=(l+r)>>1;
            int summ=sum[ll[a]]+sum[ll[b]]-sum[ll[c]]-sum[ll[d]];
            if (summ>=k) r=mid,a=ll[a],b=ll[b],c=ll[c],d=ll[d];
                else k-=summ,l=mid+1,a=rr[a],b=rr[b],c=rr[c],d=rr[d];
        }
    return l;
}
int main()
{
    n=read(),m=read();
    for (int i=1; i<=n; i++) val[i]=read(),ls[i]=val[i];
    sort(ls+1,ls+n+1);
    for (int i=1; i<=n; i++) if (ls[i]!=ls[i-1]) ls[++num]=ls[i];
    for (int i=1; i<=n; i++) val[i]=lower_bound(ls+1,ls+num+1,val[i])-ls;
    for (int u,v,i=1; i<=n-1; i++)
        u=read(),v=read(),insert(u,v);
    dfs(1);
    for (int tmp,i=1; i<=n; i++)
        tmp=steck[i],update(1,num,root[i],root[pos[father[tmp][0]]],val[tmp]);
    int lastans=0,ans=0;
    for (int u,v,k,i=1; i<=m; i++)
        {
            u=read(),v=read(),k=read(); u^=lastans;
            ans=query(1,num,u,v,k); ans=ls[ans];
            printf("%d",ans); if (i!=m) puts("");
            lastans=ans;
        }
    return 0;
} 

你丫这题卡最后一行的行末换行...强行PE...好像切题又被蛋蛋发现了...

时间: 2024-10-23 13:42:06

【BZOJ-2588】Count on a tree 主席树 + 倍增的相关文章

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

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

【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 解题报告

Count on a tree 题目描述 给定一棵\(N\)个节点的树,每个点有一个权值,对于\(M\)个询问\((u,v,k)\),你需要回答\(u\) \(xor\) \(lastans\)和\(v\)这两个节点间第\(K\)小的点权.其中\(lastans\)是上一个询问的答案,初始为\(0\),即第一个询问的u是明文. 输入输出格式 输入格式: 第一行两个整数\(N,M\). 第二行有\(N\)个整数,其中第\(i\)个整数表示点\(i\)的权值. 后面\(N-1\)行每行两个整数\((

bzoj 2588 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

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

BZOJ 2588 Count on a tree (COT) 是持久的段树

标题效果:两棵树之间的首次查询k大点的权利. 思维:树木覆盖树,事实上,它是正常的树木覆盖了持久段树. 由于使用权值段树可以寻求区间k大,然后应用到持久段树思想,间隔可以做减法.详见代码. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define NIL (tree[0]) using names