[BZOJ 4771]七彩树(可持久化线段树+树上差分)

题面

给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点。每个节点都被染上了某一种颜色,其中第i个节点的颜色为c[i]。如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色。定义depth[i]为i节点与根节点的距离。为了方便起见,你可以认为树上相邻的两个点之间的距离为1。站在这棵色彩斑斓的树前面,你将面临m个问题。

每个问题包含两个整数x和d,表示询问x子树里且depth不超过depth[x]+d的所有点中出现了多少种本质不同的颜色。请写一个程序,快速回答这些询问。

分析

先不考虑深度限制,我们考虑如何统计颜色。

我们把每种颜色开一个set,set里存储该颜色节点的dfs序。对于一对相同颜色的节点u,v,他们会对u到v的路径上的节点,和lca(u,v)到根节点路径上的节点的答案产生1的贡献。可以用树上差分算法。开一棵线段树,线段树第x个叶子节点存储节点dfs序(记作dfn) 为x的差分值,维护区间和。那么我们把dfn[u]+1,dfn[v]+1,dfn[lca(u,v)]-1即可。查询x子树的时候直接区间查询x的dfs序对应的区间。

那么我们加入一个新节点x的时候如何更新答案呢。这实际上是在处理树链的并。我们在set中找出x的前驱pre和后继nex(按照dfn排序后),dfn[x]+1,dfn[lca(pre,x)]-1,相当于把x到lca(pre,x)的路径加入答案。同理对nex进行操作。注意lca(pre,nex)往上的路径被减了两次,所以dfn[lca(pre,nex)] -1.这里画个图可以方便理解

那么有深度限制要怎么做呢?维护可持久化线段树,第i棵可持久化线段树存储深度<=i的树的答案,把节点按深度排序。依次加入对应深度的可持久化线段树。查询的时候直接在对应的线段树中查询x子树即可。

代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<cmath>
  6. #include<set>
  7. #define maxn 400000
  8. #define maxlogn 32
  9. using namespace std;
  10. inline int qread(int &x){
  11. x=0;
  12. int sign=1;
  13. char c=getchar();
  14. while(c<'0'||c>'9'){
  15. if(c=='-') sign=-1;
  16. c=getchar();
  17. }
  18. while(c>='0'&&c<='9'){
  19. x=x*10+c-'0';
  20. c=getchar();
  21. }
  22. return x*sign;
  23. }
  24. inline void qprint(int x){
  25. if(x<0){
  26. putchar('-');
  27. qprint(-x);
  28. }else if(x==0){
  29. putchar('0');
  30. return;
  31. }else{
  32. if(x/10>0) qprint(x/10);
  33. putchar('0'+x%10);
  34. }
  35. }
  36. int t,n,m;
  37. int c[maxn+5];
  38. struct edge{
  39. int from;
  40. int to;
  41. int next;
  42. }E[maxn*2+5];
  43. int sz=1;
  44. int head[maxn+5];
  45. void add_edge(int u,int v){
  46. sz++;
  47. E[sz].from=u;
  48. E[sz].to=v;
  49. E[sz].next=head[u];
  50. head[u]=sz;
  51. }
  52. int log2n;
  53. int tim;
  54. int anc[maxn+5][maxlogn+5];
  55. int hash_dfn[maxn+5];
  56. int deep[maxn+5];
  57. int lb[maxn+5],rb[maxn+5];
  58. int id[maxn+5];
  59. int cmp(int x,int y){
  60. return deep[x]<deep[y];
  61. }
  62. void dfs(int x,int fa){
  63. tim++;
  64. lb[x]=tim;
  65. deep[x]=deep[fa]+1;
  66. anc[x][0]=fa;
  67. hash_dfn[lb[x]]=x;
  68. for(int i=1;i<=log2n;i++) anc[x][i]=anc[anc[x][i-1]][i-1];
  69. for(int i=head[x];i;i=E[i].next){
  70. int y=E[i].to;
  71. if(y!=fa){
  72. dfs(y,x);
  73. }
  74. }
  75. rb[x]=tim;
  76. }
  77. int lca(int x,int y){
  78. if(deep[x]<deep[y]) swap(x,y);
  79. for(int i=log2n;i>=0;i--){
  80. if(deep[anc[x][i]]>=deep[y]){
  81. x=anc[x][i];
  82. }
  83. }
  84. if(x==y) return x;
  85. for(int i=log2n;i>=0;i--){
  86. if(anc[x][i]!=anc[y][i]){
  87. x=anc[x][i];
  88. y=anc[y][i];
  89. }
  90. }
  91. return anc[x][0];
  92. }
  93. struct per_segment_tree{
  94. //第i棵可持久化线段树维护deep<=i,且dfn在[l,r]内的节点的不同颜色个数
  95. struct node{
  96. int ls;
  97. int rs;
  98. int cnt;
  99. }tree[maxn*maxlogn+5];
  100. int root[maxn+5];
  101. int ptr;
  102. void push_up(int x){
  103. tree[x].cnt=tree[tree[x].ls].cnt+tree[tree[x].rs].cnt;
  104. }
  105. void insert(int &x,int last,int upos,int uval,int l,int r){
  106. x=++ptr;
  107. tree[x]=tree[last];
  108. if(l==r){
  109. tree[x].cnt+=uval;
  110. return;
  111. }
  112. int mid=(l+r)>>1;
  113. if(upos<=mid) insert(tree[x].ls,tree[last].ls,upos,uval,l,mid);
  114. else insert(tree[x].rs,tree[last].rs,upos,uval,mid+1,r);
  115. push_up(x);
  116. }
  117. int query(int x,int L,int R,int l,int r){
  118. if(x==0) return tree[x].cnt;
  119. if(L<=l&&R>=r){
  120. return tree[x].cnt;
  121. }
  122. int mid=(l+r)>>1;
  123. int ans=0;
  124. if(L<=mid) ans+=query(tree[x].ls,L,R,l,mid);
  125. if(R>mid) ans+=query(tree[x].rs,L,R,mid+1,r);
  126. return ans;
  127. }
  128. }T;
  129. set<int>s[maxn+5];
  130. void ini(){
  131. log2n=log2(n)+1;
  132. tim=0;
  133. sz=1;
  134. T.ptr=0;
  135. for(int i=1;i<=n;i++){//不要memset,会TLE
  136. anc[i][0]=0;
  137. c[i]=0;
  138. deep[i]=0;
  139. hash_dfn[i]=0;
  140. head[i]=0;
  141. id[i]=0;
  142. lb[i]=0;
  143. rb[i]=0;
  144. s[i].clear();
  145. T.root[i]=0;
  146. }
  147. }
  148. int main(){
  149. int u,v;
  150. qread(t);
  151. while(t--){
  152. qread(n);
  153. qread(m);
  154. ini();
  155. for(int i=1;i<=n;i++) qread(c[i]);
  156. for(int i=2;i<=n;i++){
  157. qread(u);
  158. add_edge(u,i);
  159. add_edge(i,u);
  160. }
  161. dfs(1,0);
  162. for(int i=1;i<=n;i++) id[i]=i;
  163. sort(id+1,id+1+n,cmp);
  164. for(int i=1;i<=n;i++){
  165. int x=id[i];
  166. int pre=0,nex=0;
  167. T.insert(T.root[deep[x]],T.root[deep[id[i-1]]],lb[x],1,1,n);
  168. set<int>::iterator it=s[c[x]].lower_bound(lb[x]);
  169. if(it!=s[c[x]].begin()){
  170. set<int>::iterator it2=it;//不能直接--it,会影响下一个判断
  171. pre=hash_dfn[*(--it2)];
  172. T.insert(T.root[deep[x]],T.root[deep[x]],lb[lca(pre,x)],-1,1,n);
  173. //x会对pre到x的路径上的点产生1的贡献,树上差分
  174. //线段树里的每个店储存的都是差分值
  175. }
  176. if(it!=s[c[x]].end()){
  177. nex=hash_dfn[*it];
  178. T.insert(T.root[deep[x]],T.root[deep[x]],lb[lca(x,nex)],-1,1,n);
  179. }
  180. //lca(pre,nex)上方的路径被多减了一次,加回来
  181. if(pre!=0&&nex!=0){
  182. T.insert(T.root[deep[x]],T.root[deep[x]],lb[lca(pre,nex)],1,1,n);
  183. }
  184. s[c[x]].insert(lb[x]);
  185. }
  186. int last=0;
  187. int x,d;
  188. for(int i=1;i<=m;i++){
  189. qread(x);
  190. qread(d);
  191. x^=last;
  192. d^=last;
  193. last=T.query(T.root[min(deep[x]+d,deep[id[n]])],lb[x],rb[x],1,n);
  194. qprint(last);
  195. putchar('\n');
  196. }
  197. }
  198. }

[BZOJ 4771]七彩树(可持久化线段树+树上差分)的更多相关文章

  1. 主席树||可持久化线段树+离散化 || 莫队+分块 ||BZOJ 3585: mex || Luogu P4137 Rmq Problem / mex

    题面:Rmq Problem / mex 题解: 先离散化,然后插一堆空白,大体就是如果(对于以a.data<b.data排序后的A)A[i-1].data+1!=A[i].data,则插一个空 ...

  2. BZOJ.4771.七彩树(可持久化线段树)

    BZOJ 考虑没有深度限制,对整棵子树询问怎么做. 对于同种颜色中DFS序相邻的两个点\(u,v\),在\(dfn[u],dfn[v]\)处分别\(+1\),\(dfn[LCA(u,v)]\)处\(- ...

  3. BZOJ 4771: 七彩树 可持久化线段树+树链的并

    这个思路挺有意思的 ~ 利用树链的并来保证每个颜色只贡献一次,然后用可持久化线段树维护 code: #include <set> #include <cstdio> #incl ...

  4. BZOJ4771七彩树——可持久化线段树+set+树链的并+LCA

    给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节 点的颜色为c[i].如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色.定义dept ...

  5. BZOJ 3483 SGU505 Prefixes and suffixes(字典树+可持久化线段树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3483 [题目大意] 给出一些串,同时给出m对前缀后缀,询问有多少串满足给出的前缀后缀模 ...

  6. bzoj 2653 二分答案+可持久化线段树

    首先离散化,然后我们知道如果对于一个询问的区间[l1,r1],[l2,r2],我们二分到一个答案x,将[l1,r2]区间中的元素大于等于x的设为1,其余的设为-1,那么如果[l1,r1]的最大右区间和 ...

  7. BZOJ 3439 Kpm的MCpassword Trie树+可持久化线段树

    题目大意:给定n个字符串,对于每一个字符串求以这个字符串为后缀的字符串中第k小的编号 首先将字符串反转 那么就变成了对于每一个字符串求以这个字符串为前缀的字符串中第k小的编号 然后考虑对字符串排序 那 ...

  8. 归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

    如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k ...

  9. 主席树[可持久化线段树](hdu 2665 Kth number、SP 10628 Count on a tree、ZOJ 2112 Dynamic Rankings、codeforces 813E Army Creation、codeforces960F:Pathwalks )

    在今天三黑(恶意评分刷上去的那种)两紫的智推中,突然出现了P3834 [模板]可持久化线段树 1(主席树)就突然有了不详的预感2333 果然...然后我gg了!被大佬虐了! hdu 2665 Kth ...

随机推荐

  1. 行人重识别(ReID) ——基于Person_reID_baseline_pytorch修改业务流程

    下载Person_reID_baseline_pytorch地址:https://github.com/layumi/Person_reID_baseline_pytorch/tree/master/ ...

  2. MYSQL学习笔记——常用语句

    1.检索数据 1.1.检索单个列:SELECT prod_name FROM products; 1.2.检索多个列:SELECT prod_id, prod_name, prod_price FRO ...

  3. 补比赛——牛客OI周赛9-普及组

    比赛地址 A 小Q想撸串 题目分析 普及T1水题惯例.字符串中找子串. Code #include<algorithm> #include<iostream> #include ...

  4. 查看Linux系统所对应的版本

    #cat /etc/issue 在CentOS下执行显示为:CentOS release 5.7 (Final)Kernel \r on an \m 或在Ubuntu下显示为:Ubuntu 11.04 ...

  5. [python 学习] 类

    #!/usr/bin/python # -*- encoding:utf-8 -*- class Animal: animal_num = 0 class Dog(Animal): #类帮助文档 't ...

  6. php 调用远程url

    // ; Whether to allow the treatment of URLs (like http:// or ftp://) as files. // ; http://php.net/a ...

  7. thinkphp 数据库连接报错 SQLSTATE[HY000] [2002] No such file or directory

    https://blog.csdn.net/tornge/article/details/51388233 找到mysql sokcet的路径 vim /etc/mysql/mysql.conf.d/ ...

  8. configerparser模块

    '''[mysqld]charater-server-set='utf8'default-engine='innodb'skip-grant-table=Trueport=3306 [client]u ...

  9. re模块 时间模块

    # 正则模块'''正则就是用一些具有特殊含义的符号组合到一起用来描述字符或字符串的方法或者说,正则就是用来描述一类事物的规则它内嵌在python中,并通过re模块实现正则表达式模式被编译成一系列的字节 ...

  10. 记录ViewPager配合Fragment使用中遇到的一个问题

    java.lang.IllegalStateException: FragmentManager is already executing transactions 如图所示: 当调用 notifyD ...