[BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并)

题面

N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.

\(n,m\leq 1 \times 10^5\),颜色编号 \(\leq 1 \times 10^6\)

分析

先考虑询问,我们可以对每种颜色分别求出这种颜色的连续段有多少个。可以用权值线段树实现。第c棵权值线段树维护颜色c的位置出现情况,如果第i个位置颜色为c.则线段树上[i,i]为1。

这样我们就只要维护一下区间连续段个数就可以了。每个区间维护3个值:lv,rv,sum,分别表示左端点是否为1,右端点是否为1和总段数,合并两个区间的时候如果如果左区间的右端点和右区间的左端点为1,那么两段可以拼成一段连续的,答案为两个区间的段数之和-1,否则 答案为两个区间的段数之和。

把所有颜色的线段树的答案(根节点sum值)加起来,就是初始的颜色段数。

然后考虑修改。如果我们把颜色x变成颜色y,相当于把x对应的线段树合并到y对应的线段树上,然后删除线段树x。合并就是模板的线段树合并,复杂度\(O(\log n)\),而删除只需要把x对应的线段树的根设成0即可,复杂度是\(O(1)\)的。每次都求一遍答案显然会超时,我们只需要考虑一次修改操作对答案的变化量。我们先减去线段树x,y的答案,合并后再加上线段树y的答案即可

总时间复杂度\(O(m \log n)\)

代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #define maxn 100000
  5. #define maxlogc 25
  6. #define maxc 1000000
  7. using namespace std;
  8. int n,m;
  9. int a[maxn+5];
  10. struct segment_tree{
  11. //每棵线段树维护一种颜色c的位置出现情况,如果第i个位置颜色为c.则线段树上[i,i]为1
  12. #define lson(x) tree[x].ls
  13. #define rson(x) tree[x].rs
  14. struct node{
  15. int ls;
  16. int rs;
  17. int lv;//区间左端点是否为1
  18. int rv;//区间右端点是否为1
  19. int sum;//总段数
  20. }tree[maxn*maxlogc+5];
  21. int ptr;
  22. void push_up(int x){
  23. tree[x].lv=tree[lson(x)].lv;
  24. tree[x].rv=tree[rson(x)].rv;
  25. tree[x].sum=tree[lson(x)].sum+tree[rson(x)].sum-(tree[lson(x)].rv==1&&tree[rson(x)].lv==1);
  26. //如果左区间的右侧和右区间的左侧为1,那么两段可以拼成一段连续的,答案-1
  27. }
  28. void update(int &x,int upos,int l,int r){
  29. if(!x) x=++ptr;
  30. if(l==r){
  31. tree[x].lv=tree[x].rv=tree[x].sum=1;
  32. return;
  33. }
  34. int mid=(l+r)>>1;
  35. if(upos<=mid) update(tree[x].ls,upos,l,mid);
  36. else update(tree[x].rs,upos,mid+1,r);
  37. push_up(x);
  38. }
  39. int merge(int x,int y,int l,int r){
  40. if(!x||!y) return x+y;
  41. if(l==r){
  42. tree[x].sum|=tree[y].sum;
  43. tree[x].lv|=tree[y].lv;
  44. tree[x].rv|=tree[y].rv;
  45. return x;
  46. }
  47. int mid=(l+r)>>1;
  48. tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid);
  49. tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r);
  50. push_up(x);
  51. return x;
  52. }
  53. }T;
  54. int root[maxc+5];
  55. int main(){
  56. int cmd,x,y;
  57. scanf("%d %d",&n,&m);
  58. for(int i=1;i<=n;i++){
  59. scanf("%d",&a[i]);
  60. T.update(root[a[i]],i,1,n);
  61. }
  62. int ans=0;
  63. for(int i=1;i<=maxc;i++) ans+=T.tree[root[i]].sum;
  64. for(int i=1;i<=m;i++){
  65. scanf("%d",&cmd);
  66. if(cmd==1){
  67. scanf("%d %d",&x,&y);
  68. if(x==y) continue;
  69. ans-=T.tree[root[x]].sum;
  70. ans-=T.tree[root[y]].sum;
  71. root[y]=T.merge(root[y],root[x],1,n);//一定要写root[y]=
  72. ans+=T.tree[root[y]].sum;
  73. root[x]=0; //记得删除x这棵树
  74. }
  75. else printf("%d\n",ans);
  76. }
  77. }

[BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并)的更多相关文章

  1. BZOJ 1483: [HNOI2009]梦幻布丁( 链表 + 启发式合并 )

    把相同颜色的串成一个链表, 然后每次A操作就启发式合并, 然后计算对答案的影响. ----------------------------------------------------------- ...

  2. BZOJ 1483: [HNOI2009]梦幻布丁 [链表启发式合并]

    1483: [HNOI2009]梦幻布丁 题意:一个带颜色序列,一种颜色合并到另一种,询问有多少颜色段 一种颜色开一个链表,每次遍历小的合并到大的里,顺带维护答案 等等,合并方向有规定? 令col[x ...

  3. bzoj 1483: [HNOI2009]梦幻布丁 (链表启发式合并)

    Description N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色. 例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. Input ...

  4. bzoj 1483 [HNOI2009]梦幻布丁(链表+启发式合并)

    1483: [HNOI2009]梦幻布丁 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1818  Solved: 761[Submit][Status ...

  5. bzoj 1483: [HNOI2009]梦幻布丁 启发式合并vector

    1483: [HNOI2009]梦幻布丁 Time Limit: 10 Sec  Memory Limit: 64 MB[Submit][Status][Discuss] Description N个 ...

  6. bzoj 1483: [HNOI2009]梦幻布丁

    1483: [HNOI2009]梦幻布丁 Description N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1 ...

  7. BZOJ 1483 HNOI2009 梦幻布丁 名单+启示录式的合并

    标题效果:特定n布丁.每个人都有一个颜色布丁,所有的布丁反复有一定的颜色变化的颜色,颜色反复询问的段数 数据范围:n<=10W 色彩数<=100W 启发式合并名单0.0 从来不写清楚 实际 ...

  8. 1483: [HNOI2009]梦幻布丁

    1483: [HNOI2009]梦幻布丁 链接 分析: 启发式合并+链表. 代码: #include<cstdio> #include<algorithm> #include& ...

  9. bzoj1483: [HNOI2009]梦幻布丁(vector+启发式合并)

    1483: [HNOI2009]梦幻布丁 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 4022  Solved: 1640[Submit][Statu ...

随机推荐

  1. 扩展微信小程序 Page 构造函数,修改生命周期函数

    不BB,直接正题 一. 将公共方法绑定到Page上 单个绑定 const oldPage = Page Page = function(app) { // 注意公共函数的名字不要重复,否则覆盖 app ...

  2. 【转载】 原生js判断某个元素是否滚动到底部

    document.querySelector('.content').addEventListener('scroll',function () { //读取内容区域的真实高度(滚动条高) // co ...

  3. App 区别

    本文的结构主要分为以下部分: 1.app的分类 2.每类app的定义,明确各类app具体是什么 3.各类app的优缺点 4.具体开发过程中,到底该采用哪种类型的app 1.app的分类 大致可以分为这 ...

  4. js设置定时器在规定的日期内替换掉页面

    <script type="text/javascript"> window.onload=function(){ var myspan=document.getEle ...

  5. Redis学习:Redis的安装与配置

    Redis是新兴的一种内存数据库技术,在数据高速读写方面有着明显的优势.前几天,Redis3.0正式版本发布,为我们带来了Redis集群功能.这一功能很早就投入了开发,直到现在才真正走进我们的视野.可 ...

  6. Spring Cloud Commons教程(一)普通抽象

    诸如服务发现,负载平衡和断路器之类的模式适用于所有Spring Cloud客户端可以独立于实现(例如通过Eureka或Consul发现)的消耗的共同抽象层. @EnableDiscoveryClien ...

  7. codeforces D Salary Changing

    题意:给你n个人,和s块钱,每个人都有一个工资区间,你给所有人都发工资.然后要他们工资的中位数最大. 思路:二分找那个值.那个值要满足至少有n/2+1个工资区间内. #include<cstdi ...

  8. 170911-关于maven的知识点

    简介:    什么是maven 是apache下的一个开源项目,是纯java开发,并且只是用来管理java项目的. Maven的好处? 节省空间 就是对jar包的统一管理就是对jar包的统一管理,依赖 ...

  9. Linux日志管理系统rsyslog

    一.日志的概念 什么是日志?日志就是历史事件.历史事件包括时间.地点.人物.时间.这个是生活中所说的日志很好理解.在Linux中也有类似的服务,它主要作用就是记录Linux系统的历史事件,包括什么时间 ...

  10. 获取项目配置的常用方法(Struts/Servlet)

    struts: //web.xml中: <context-param> <param-name>paramName</param-name> <param-v ...