【树上主席树】BZOJ2588-Count on a tree

【题目大意】

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

【思路】

这道题迷之好写,因为思路条理太清晰了!

我们每个点就是一棵线段树,维护它到根的每个数字的个数,但是这样会MLE所以自然而然地用主席树来维护。

u->v路径上每种的个数就等于sum[u]-sum[lca(u,v)]+sum[v]-sum[fa[lca(u,v)]]。

写起来特别爽。

然而我RE了一个上午。接着突然发现题意“(u,v)表示u到v有一条边)它居然是无向的??天真地以为有向u->v,调出了一开始的程序,默默地改掉,默默地AC...

还我两小时的人生!!!!

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #define lson l,m
  7 #define rson m+1,r
  8 using namespace std;
  9 const int MAXN=100000+500;
 10 const int DEG=20;
 11 int w[MAXN];
 12 vector<int> E[MAXN];
 13 int d,hash[MAXN];
 14 int T[MAXN],tot,sum[MAXN<<5],L[MAXN<<5],R[MAXN<<5];
 15 int anc[MAXN][DEG],dep[MAXN];
 16 int n,m;
 17
 18 /*Chairman Tree*/
 19 int build(int l,int r)
 20 {
 21     int rt=++tot;
 22     sum[rt]=0;
 23     if (l!=r)
 24     {
 25         int m=(l+r)>>1;
 26         L[rt]=build(lson);
 27         R[rt]=build(rson);
 28     }
 29     return rt;
 30 }
 31
 32 int update(int pre,int l,int r,int x)
 33 {
 34     int rt=++tot;
 35     L[rt]=L[pre],R[rt]=R[pre];
 36     sum[rt]=sum[pre]+1;
 37     if (l!=r)
 38     {
 39         int m=(l+r)>>1;
 40         if (x<=m) L[rt]=update(L[pre],lson,x);
 41             else R[rt]=update(R[pre],rson,x);
 42     }
 43     return rt;
 44 }
 45
 46 int query(int u,int v,int lca,int lcafa,int l,int r,int k)
 47 {
 48     if (l==r) return l;
 49     int num=(sum[L[u]]-sum[L[lca]]+sum[L[v]]-sum[L[lcafa]]);
 50     int m=(l+r)>>1;
 51     if (num>=k) return query(L[u],L[v],L[lca],L[lcafa],lson,k);
 52         else return query(R[u],R[v],R[lca],R[lcafa],rson,k-num);
 53 }
 54
 55 /*LCA*/
 56 void getanc()
 57 {
 58     for (int i=1;i<DEG;i++)
 59         for (int j=1;j<=n;j++)
 60             anc[j][i]=anc[anc[j][i-1]][i-1];
 61 }
 62
 63 int swim(int x,int H)
 64 {
 65     for (int i=0;H>0;i++)
 66     {
 67         if (H&1) x=anc[x][i];
 68         H>>=1;
 69     }
 70     return x;
 71 }
 72
 73 int LCA(int u,int v)
 74 {
 75     if (dep[u]<dep[v]) swap(u,v);
 76     u=swim(u,dep[u]-dep[v]);
 77     if (u==v) return u;
 78     for (int i=DEG-1;i>=0;i--)
 79     {
 80         if (anc[u][i]!=anc[v][i])
 81         {
 82             u=anc[u][i];
 83             v=anc[v][i];
 84         }
 85     }
 86     return anc[u][0];
 87 }
 88
 89 /*main*/
 90 void dfs(int u,int pa,int depth)
 91 {
 92     anc[u][0]=pa;
 93     dep[u]=depth;
 94     int x=lower_bound(hash+1,hash+d+1,w[u])-hash;
 95     T[u]=update(T[pa],1,d,x);
 96     for (int i=0;i<E[u].size();i++)
 97         if (E[u][i]!=pa) dfs(E[u][i],u,depth+1);
 98 }
 99
100 void init()
101 {
102     scanf("%d%d",&n,&m);
103     for (int i=1;i<=n;i++) scanf("%d",&w[i]),hash[i]=w[i];
104     sort(hash+1,hash+n+1);
105     d=unique(hash+1,hash+n+1)-(hash+1);
106
107     for (int i=1;i<n;i++)
108     {
109         int u,v;
110         scanf("%d%d",&u,&v);
111         E[u].push_back(v);
112         E[v].push_back(u);
113     }
114
115
116     tot=0;
117     T[1]=build(1,d);//对于根先建立主席树
118 }
119
120 void solve()
121 {
122     getanc();
123     int preans=0;
124     for (int i=0;i<m;i++)
125     {
126         int u,v,k;
127         scanf("%d%d%d",&u,&v,&k);
128         u=u^preans;
129         int lca=LCA(u,v);
130         int ans=query(T[u],T[v],T[lca],T[anc[lca][0]],1,d,k);
131         printf("%d",hash[ans]);
132         if (i!=m-1) printf("\n");
133         preans=hash[ans];
134     }
135 }
136
137 int main()
138 {
139     init();
140     dfs(1,0,1);
141     solve();
142     return 0;
143 }
时间: 2024-09-30 11:13:48

【树上主席树】BZOJ2588-Count on a tree的相关文章

SPOJ COT Count on a tree(树上主席树 + LCA 求路径第k小)题解

题意:n个点的树,每个点有权值,问你u~v路径第k小的点的权值是? 思路: 树上主席树就是每个点建一棵权值线段树,具体看JQ博客,LCA用倍增logn求出,具体原理看这里 树上主席树我每个点的存的是点u到源点1的权值线段树,那我求点u到v的所有点,显然是 u + v - lca - fa[lca],就是u到1 + v到1 - 多算的lca - 多算的fa[lca].不能减去两个lca不然少一个点了, LCA板子: //LCA int fa[maxn][20]; int dep[maxn]; vo

p3302 [SDOI2013]森林(树上主席树+启发式合并)

对着题目yy了一天加上看了一中午题解,终于搞明白了我太弱了 连边就是合并线段树,把小的集合合并到大的上,可以保证规模至少增加一半,复杂度可以是\(O(logn)\) 合并的时候暴力dfs修改倍增数组和维护主席树即可 然后树上主席树就是维护节点到根节点的信息即可, 询问链上的第k大时,画图后可以发现维护一个rootx,rooty,rootlca和rootfalca即可解决问题 注意空间要开够 然后注意细节,没了 #include <cstdio> #include <cstring>

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

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

(LCA+树上主席树)FZU 2237 - 中位数

题意: 多次查询一个树链上的中位数(其实就是求K大). 分析: 感觉莫队可做,只是不会树上莫队.. 而且这里是边权,处理起来貌似有点小麻烦.. 后来发现其实貌似是一个很老的题,,kuangbin模板书上有类似的题. 树链上的第K大数,这是一道可以用主席树解的题,复杂度才nlogn. 这里也是这样先求从根到每个点的线段树,以为树存在父子关系,所有可以从让下层继承上层的线段树,非常符合主席树的可持久化思想. 然后在查询第K大的时候,去掉重复部分,就可以查了. 太强了,,, 代码: 1 #includ

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

BZOJ2588 Count on a tree

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2588 知识点: 可持久化线段树 解题思路: 先建一棵空的权值线段树,然后按照题目给出的树以任意一点为根的\(DFS\)序来更新这棵线段树.询问\((u,v,k)\)时,其实就是查询\(T[u]\)所对应的线段树加上\(T[v]\)所对应的线段树减去\(T[u and v's LCA]\)所对应的线段树再减去\(T[fa[u and v's LCA]]\),\(LCA\)就是最近公共祖先.

[SDOI2013]森林(树上主席树)

[SDOI2013]森林(luogu) Description 题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少.此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点. L x y在点x和点y之间连接一条边.保证完成此操作后,仍然是一片森林. 为了体现程序的在线性,我们把输入数据进行了加密.设lastans为程序上一次输出的结果,