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

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

树上的主席树,本来应该自已YY一下的但是没有想出来。。。尴尬。。。

其实还是很简单的。。。

每个节点以其父亲节点为历史版本建主席树,在dfs的时候insert;

那么每个点的线段树相当于是维护了从根节点到该节点路径上的点。。。

对于树上两点l,r路径上的第k大,那么要找出l,r的LCA,由于这个LCA也是需要被考虑的,所以还要求LCA的爸爸;

所以每次相当于是 s[ls[l]]+s[ls[r]]-s[ls[lca]]-s[ls[fa[lca]];

这个东西把图画出来,其实就是一个树上的前缀和思想。。。

附上代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 const int N=100050;
  7 int gi()
  8 {
  9     int x=0,flag=1;
 10     char ch=getchar();
 11     while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) flag=-1;ch=getchar();}
 12     while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
 13     return x*flag;
 14 }
 15 int hash[N],num[N],v[N],s[N*30],sz,root[N],ls[N*30],rs[N*30];
 16 int dfn[N],head[N*2],nxt[N*2],to[N*2],son[N],dep[N],size[N],fa[N],top[N];
 17 int id[N],tt,cnt,tot,n,m,last;
 18 int find(int x)
 19 {
 20     int l=1,r=tot;
 21     while(l<=r)
 22     {
 23         int mid=(l+r)>>1;
 24         if(hash[mid]<x) l=mid+1;
 25         else r=mid-1;
 26     }
 27     return l;
 28 }
 29 void insert(int l,int r,int x,int &y,int k)
 30 {
 31     y=++sz;
 32     s[y]=s[x]+1;ls[y]=ls[x],rs[y]=rs[x];
 33     if(l==r) return;
 34     int mid=(l+r)>>1;
 35     if(k<=mid) insert(l,mid,ls[x],ls[y],k);
 36     else insert(mid+1,r,rs[x],rs[y],k);
 37 }
 38 int query(int l,int r,int a,int b,int c,int d,int k)
 39 {
 40     if(l==r) return hash[l];
 41     int mid=(l+r)>>1;
 42     if(s[ls[a]]+s[ls[b]]-s[ls[c]]-s[ls[d]]>=k)
 43         return query(l,mid,ls[a],ls[b],ls[c],ls[d],k);
 44     else return query(mid+1,r,rs[a],rs[b],rs[c],rs[d],k-(s[ls[a]]+s[ls[b]]-s[ls[c]]-s[ls[d]]));
 45 }
 46 void dfs1(int x,int f)
 47 {
 48     dep[x]=dep[f]+1;size[x]=1;
 49     insert(1,tot,root[f],root[x],find(v[x]));
 50     for(int i=head[x]; i; i=nxt[i])
 51     {
 52         int y=to[i];
 53         if(y!=f)
 54         {
 55             dfs1(y,x);
 56             fa[y]=x;size[x]+=size[y];
 57             if(size[y]>size[son[x]]) son[x]=y;
 58         }
 59     }
 60 }
 61 void dfs2(int x,int f)
 62 {
 63     dfn[x]=++tt,top[x]=f;
 64     if(son[x]) dfs2(son[x],f);
 65     for(int i=head[x]; i; i=nxt[i])
 66     {
 67         int y=to[i];
 68         if(y!=fa[x]&&y!=son[x]) dfs2(y,y);
 69     }
 70 }
 71 int lca(int x,int y)
 72 {
 73     while(top[x]!=top[y])
 74     {
 75         if(dep[top[x]]<dep[top[y]]) swap(x,y);
 76         x=fa[top[x]];
 77     }
 78     if(dep[x]<dep[y]) swap(x,y);
 79     return y;
 80 }
 81 void lnk(int x,int y)
 82 {
 83     to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;
 84     to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt;
 85 }
 86 int main()
 87 {
 88     n=gi(),m=gi();
 89     for(int i=1; i<=n; i++) v[i]=gi(),num[i]=v[i];
 90     sort(num+1,num+1+n);
 91     hash[++tot]=num[1];
 92     for(int i=2; i<=n; i++)
 93         if(num[i]!=num[i-1])
 94             hash[++tot]=num[i];
 95     int x,y,k;
 96     for(int i=1; i<n; i++) x=gi(),y=gi(),lnk(x,y);
 97     dfs1(1,0);dfs2(1,1);
 98     for(int i=1; i<=m; i++)
 99     {
100         x=gi();y=gi();k=gi();x^=last;
101         int a=root[x],b=root[y],c=root[lca(x,y)],d=root[fa[lca(x,y)]];
102         last=query(1,tot,a,b,c,d,k);
103         printf("%d",last);if(i!=m) printf("\n");
104     }
105 }
时间: 2025-01-13 14:28:03

bzoj 2588 Count on a tree的相关文章

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 主席树+倍增LCA

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

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

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 name

2588. Count on a tree【主席树+LCA】

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

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

【BZOJ】【2588】COT(Count On a Tree)

可持久化线段树 maya……树么……转化成序列……所以就写了个树链剖分……然后每个点保存的是从它到根的可持久化线段树. 然后就像序列一样查询……注意是多个左端点和多个右端点,处理方法类似BZOJ 1901 然后rausen(Orz!!!)粗来跟我说:你直接减去lca和fa[lca]不就好啦~搞树剖还多一个log…… 我恍然大悟!然后两个都交了一下,事实证明:我链剖写的还行,LCA写的太丑……速度反而是多一个log的链剖快QAQ(另:因为边少我就偷懒没写边表,直接vector水过) 链剖: 1 /