4817: [Sdoi2017]树点涂色


Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 629  Solved: 371
[Submit][Status][Discuss]

Description


Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x y:
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行m次操作
 

Input


第一行两个数n,m。
接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
接下来m行,表示操作,格式见题目描述
1<=n,m<=100000
 

Output


每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值
 

Sample Input


  1.  

Sample Output


  1.  

题解:


其实这道题考的十分妙啊,对LCT的虚实边性质进行了充分的利用。

我们读完题发现操作2可以很快想到树剖,操作3可以维护dfs序在线段树上查询子树信息。
 
但是操作1并不是树剖能解决的;
 
再来看看操作1的特点 :每次只会修改一种从未出现的颜色,并且一定是从当前点修改到根节点。
 
然后神奇的操作就来了 :
 
一开始我们按原树建出lct,只是lct中全部都是虚边,一个点到根节点不同 颜色数量就是它往上到根节点经过虚边数量+1。
 
每次操作1相当于就是以把当前点accees上去,这样同样颜色的就以实边连在一起了。是不是很神奇。。
 
然后再来看看怎么统计答案,我们看看lct中什么时候会变虚实边关系,当然就是access了。
 
access时我们有一句话ch[x][1] = y ,直接把x原来的右儿子覆盖了,变成了新的右儿子,
 
那么y的虚实关系从虚转为了实,只用找到y中最浅的一个点,对它子树全部贡献-1即可。
 
然后原来的右儿子从实变为了虚,找到它最浅的一个点,对它子树全部贡献+1即可。
 
然后我们来看看操作2,是对一条路径进行查询,并不能用树剖做。
 
但是我们发现一个性质,因为每次修改的颜色都是新的颜色,并且是从当前点修改到根节点,设lca(u,v) != u && lca(u,v) != v
 
那么u和v一定颜色不同,我们只需要在线段树上单询ask(u) + ask(v) -  2 * ask(lca(u,v)) +  1 即可
 
但为什么要加那个1
 

 如图,我们查询左下角颜色为1点和右下角的颜色为2的点;当lca颜色和它们不同时发现lca的颜色减多了,要加回来。
 

当lca和其中一个相同时,我们发现还是减多了,还是得加回来。

一条链的情况自己画图也是同理的。这样对于操作2就用线段树轻松维护了。

操作3????不就是线段树dfs序查询子树吗。

这样我们就神奇的利用了LCT的性质把一道看似树剖的题做成了LCT神题。。

AC代码:

过了样例直接交,一遍交过的酸爽


  1. # include <iostream>
  2. # include <cstdio>
  3. # include <cstring>
  4. using namespace std;
  5. const int N = 2e5 + ;
  6. int st[N],ed[N],fa[N],ch[N][],s[N << ],la[N << ],n,m,head[N],dt;
  7. struct Edge{
  8. int to,nex;
  9. }edge[N << ];
  10. void AddEdge(int u,int v)
  11. {
  12. edge[++dt] = (Edge){v,head[u]};
  13. head[u] = dt;
  14. }
  15. bool isroot(int x){return ch[fa[x]][] != x && ch[fa[x]][] != x;}
  16. void rotate(int x,int d)
  17. {
  18. int pre = fa[x],g = fa[pre],nex = ch[x][d];
  19. ch[pre][d ^ ] = nex;
  20. if(nex)fa[nex] = pre;
  21. fa[x] = g;
  22. if(!isroot(pre))ch[g][ch[g][] == pre] = x;
  23. ch[x][d] = pre;
  24. fa[pre] = x;
  25. }
  26. void splay(int x)
  27. {
  28. int pre,g;
  29. while(!isroot(x))
  30. {
  31. pre = fa[x],g = fa[pre];
  32. if(!isroot(pre) && !((ch[pre][] == x) ^ (ch[g][] == pre)))rotate(pre,ch[pre][] == x);
  33. rotate(x,ch[pre][] == x);
  34. }
  35. }
  36. int find(int x){while(ch[x][])x = ch[x][];return x;}
  37. void push(int x){s[x] = max(s[x << ],s[x << | ]);}
  38. void down(int x)
  39. {
  40. s[x << ] += la[x];s[x << | ] += la[x];
  41. la[x << | ] += la[x];la[x << ] += la[x];
  42. la[x] = ;
  43. }
  44. void updata(int L,int R,int l,int r,int rt,int d)
  45. {
  46. if(L <= l && r <= R){s[rt] += d;la[rt] += d;return;}
  47. down(rt);int mid = l + r >> ;
  48. if(L <= mid)updata(L,R,l,mid,rt << ,d);
  49. if(R > mid)updata(L,R,mid + ,r,rt << | ,d);
  50. push(rt);
  51. }
  52. int Query(int L,int R,int l,int r,int rt)
  53. {
  54. if(L <= l && r <= R)return s[rt];
  55. down(rt);int mid = l + r >> ;
  56. if(L > mid)return Query(L,R,mid + ,r,rt << | );
  57. if(R <= mid)return Query(L,R,l,mid,rt << );
  58. return max(Query(L,R,l,mid,rt << ),Query(L,R,mid + ,r,rt << | ));
  59. }
  60. int ask(int x){return Query(st[x],st[x],,n,);}
  61. void access(int x)
  62. {
  63. int y = ,t;
  64. while(x)
  65. {
  66. splay(x);
  67. if(t = find(ch[x][]))updata(st[t],ed[t],,n,,);
  68. ch[x][] = y;if(t = find(y))updata(st[t],ed[t],,n,,-);
  69. y = x;x = fa[x];
  70. }
  71. }
  72. int hson[N],sz[N],tot,top[N],dep[N],Fa[N],id[N];
  73. void dfs(int u)
  74. {
  75. sz[u] = ;
  76. for(int i = head[u];i;i = edge[i].nex)
  77. {
  78. if(sz[edge[i].to])continue;
  79. Fa[edge[i].to] = u;
  80. dep[edge[i].to] = dep[u] + ;
  81. dfs(edge[i].to);
  82. sz[u] += sz[edge[i].to];
  83. if(sz[hson[u]] < sz[edge[i].to])hson[u] = edge[i].to;
  84. }
  85. }
  86. void dfs(int u,int tp)
  87. {
  88. top[u] = tp;st[u] = ++tot;id[tot] = u;
  89. if(hson[u])dfs(hson[u],tp);
  90. for(int i = head[u];i;i = edge[i].nex)
  91. if(!st[edge[i].to])dfs(edge[i].to,edge[i].to);
  92. ed[u] = tot;
  93. }
  94. int lca(int x,int y)
  95. {
  96. while(top[x] != top[y])
  97. {
  98. if(dep[top[x]] < dep[top[y]])swap(x,y);
  99. x = Fa[top[x]];
  100. }
  101. return dep[x] < dep[y] ? x : y;
  102. }
  103. int Q1(int u,int v){return ask(u) + ask(v) - * ask(lca(u,v)) + ;}
  104. int Q2(int u){return Query(st[u],ed[u],,n,);}
  105. int main()
  106. {
  107. scanf("%d %d",&n,&m);int tp,x,y;
  108. for(int i = ;i < n;i++)
  109. {
  110. scanf("%d %d",&x,&y);
  111. AddEdge(x,y);AddEdge(y,x);
  112. }
  113. dfs();dfs(,);
  114. for(int i = ;i <= n;i++)updata(st[i],ed[i],,n,,),fa[i] = Fa[i];
  115. while(m--)
  116. {
  117. scanf("%d",&tp);
  118. if(tp == )scanf("%d",&x),access(x);
  119. if(tp == )scanf("%d %d",&x,&y),printf("%d\n",Q1(x,y));
  120. if(tp == )scanf("%d",&x),printf("%d\n",Q2(x));
  121. }
  122. }

[Bzoj4817] [Sdoi2017]树点涂色 (LCT神题)的更多相关文章

  1. [BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)

    4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 692  Solved: 408[Submit][Status ...

  2. BZOJ4817: [Sdoi2017]树点涂色(LCT)

    Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. ...

  3. BZOJ4817[Sdoi2017]树点涂色——LCT+线段树

    题目描述 Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进 ...

  4. 【BZOJ4817】[Sdoi2017]树点涂色 LCT+线段树

    [BZOJ4817][Sdoi2017]树点涂色 Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路 ...

  5. [Sdoi2017]树点涂色 [lct 线段树]

    [Sdoi2017]树点涂色 题意:一棵有根树,支持x到根染成新颜色,求x到y颜色数,求x子树里点到根颜色数最大值 考场发现这个信息是可减的,但是没想到lct 特意设计成lct的形式! 如何求颜色数? ...

  6. BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)

    题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$LCT$维护一个树上集合就好 因为它维护了树上集合,所以它别的啥都干不了了 发现树是静态的 ...

  7. 【bzoj4817】树点涂色 LCT+线段树+dfs序

    Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. ...

  8. BZOJ4817 [Sdoi2017]树点涂色 【LCT + 线段树】

    题目 Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进行这 ...

  9. 【bzoj4817】[Sdoi2017]树点涂色 LCT+LCA+线段树

    题目描述 给出一棵n个点,以1为根的有根树,每个点初始染有互不相同的颜色.定义一条路径的权值为路径上的颜色种类数.现有m次操作,每次操作为以下三种之一: 1 x: 把点x到根节点的路径上所有的点染上一 ...

随机推荐

  1. php配置之include_path

    在php.ini中配置include_path,可在引入文件时直接引入配置目录下的文件. 项目中就可以直接 引入/var/www/phpxwlib及/var/www/huicuiserver/libs ...

  2. UVA 10900 So you want to be a 2n-aire? 2元富翁 (数学期望,贪心)

    题意:你一开始有1元钱,接下来又n<=30个问题,只需答对1个问题手上的钱就翻倍,最多答对n个,得到的钱是2n.而每个问题答对的概率是[t,1]之间平均分布,那么问最优情况下得到奖金的期望值是多 ...

  3. # Transition:添加弹出过渡效果

    # Transition:添加弹出过渡效果 通过鼠标的单击.获得焦点,被点击或对元素任何改变中触发,并平滑地以动画效果改变CSS的属性值. W3C-transition MDN-transition ...

  4. C++#pragma pack指令

    微软官方文档说#pragma pack 指令的作用是为结构.联合和类成员指定 pack 对齐.的主要作用就是改变编译器的内存对齐方式,这个指令在网络报文的处理中有着重要的作用,#pragma pack ...

  5. git 本地与远程仓库出现代码冲突解决方法

    提交过程中报错: [python@heaven-00 Selesystem]$ git push -u origin masterUsername for 'https://github.com': ...

  6. 下拉列表事件 Dropdown iview

    <Dropdown @on-click="export"> <Button icon='md-log-out'> 000l <Icon type=&q ...

  7. Eclipse Code Recommenders 自动补全(联想)神器

    Eclipse Code Recommenders 可以在eclipse市场中下载.自动补全.模糊匹配.非常有用!

  8. 基于jquery的自定义显示消息数量

    根据需求简单的实现一个小功能控件,暂时不支持扩展 $("xxxxxxx").iconCountPlugin(options, start, isOffset) {//三个参数,自定 ...

  9. Java 垃圾回收机制 (分代垃圾回收ZGC)

    什么是自动垃圾回收? 自动垃圾回收是一种在堆内存中找出哪些对象在被使用,还有哪些对象没被使用,并且将后者删掉的机制.所谓使用中的对象(已引用对象),指的是程序中有指针指向的对象:而未使用中的对象(未引 ...

  10. windows中彻底卸载mysql

    1)停止MySQL服务 打开控制面板→管理工具→服务(找到MySQL) 2)卸掉mysql server 打开控制面板→程序与功能→找到mysql server然后卸载 3)将mysql目录下的安装文 ...