校门外的树

描述

校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……
如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:
K=1,K=1,读入l、r表示在区间[l,r]中种上一种树,每次操作种的树的种类都不同
K=2,读入l,r表示询问l~r之间能见到多少种树
(l,r>0)

格式

输入格式

第一行n,m表示道路总长为n,共有m个操作
接下来m行为m个操作

输出格式

对于每个k=2输出一个答案

样例1

样例输入1

  1. 5 4
  2. 1 1 3
  3. 2 2 5
  4. 1 2 4
  5. 2 3 5

样例输出1

  1. 1
  2. 2

限制

1s

提示

范围:20%的数据保证,n,m<=100
60%的数据保证,n <=1000,m<=50000
100%的数据保证,n,m<=50000

来源

dejiyu@CSC WorkGroup

题目链接:https://vijos.org/p/1448

分析:这题目从上午九点写到下午四点,历经七个小时的磨难,只为给大家提供最优质的方法!

这道题我用了三种方法去解决!

第一种:线段树【时间花费最长,也最伤脑的写法】,做法是将[a,b]种上一种树,这个修改操作影响的询问满足,

询问区间与[a,b]有交,转化为统计总修改数-与某询问交为空集的修改数

对于一个修改操作[l,r],与它为空集的询问[a,b]满足a∈[1,l-1]或者b∈[r+1,n]

用两棵线段树维护,修改[l,r],将第一棵的[1,l-1]区间+1,第二棵[r+1,n]区间+1

询问[a,b],答案为之前的修改数-(第一棵单点询问b+第二棵单点询问a)

代码中线段树结点的l,r其实就是两棵线段树。。。标记永久化

下面给出线段树的代码:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N=;
  4. int n,m;
  5. inline int read()
  6. {
  7. int x=,f=;
  8. char ch=getchar();
  9. while(ch<''||ch>'')
  10. {
  11. if(ch=='-')
  12. f=-;
  13. ch=getchar();
  14. }
  15. while(ch>=''&&ch<='')
  16. {
  17. x=x*+ch-'';
  18. ch=getchar();
  19. }
  20. return x*f;
  21. }
  22. inline void write(int x)
  23. {
  24. if(x<)
  25. {
  26. putchar('-');
  27. x=-x;
  28. }
  29. if(x>)
  30. {
  31. write(x/);
  32. }
  33. putchar(x%+'');
  34. }
  35. struct Tree
  36. {
  37. int l,r;
  38. int left,right;
  39. }tree[N<<];
  40. inline void buildtree(int x,int y,int pos)
  41. {
  42. tree[pos].left=x;
  43. tree[pos].right=y;
  44. if(x==y)
  45. {
  46. return;
  47. }
  48. int mid=(x+y)/;
  49. buildtree(x,mid,pos*);
  50. buildtree(mid+,y,pos*+);
  51. }
  52. inline void insertl(int x,int y,int pos)
  53. {
  54. int l=tree[pos].left;
  55. int r=tree[pos].right;
  56. if(l==x&&r==y)
  57. {
  58. tree[pos].l++;
  59. return;
  60. }
  61. int mid=(l+r)/;
  62. if(y<=mid)
  63. insertl(x,y,pos*);
  64. else if(x>mid)
  65. insertl(x,y,pos*+);
  66. else
  67. {
  68. insertl(x,mid,pos*);
  69. insertl(mid+,y,pos*+);
  70. }
  71. }
  72. inline void insertr(int x,int y,int pos)
  73. {
  74. int l=tree[pos].left;
  75. int r=tree[pos].right;
  76. if(l==x&&r==y)
  77. {
  78. tree[pos].r++;
  79. return;
  80. }
  81. int mid=(l+r)/;
  82. if(y<=mid)
  83. insertr(x,y,pos*);
  84. else if(x>mid)
  85. insertr(x,y,pos*+);
  86. else
  87. {
  88. insertr(x,mid,pos*);
  89. insertr(mid+,y,pos*+);
  90. }
  91. }
  92. inline int askl(int k,int x)
  93. {
  94. int l=tree[k].left;
  95. int r=tree[k].right;
  96. if(l==r)
  97. return tree[k].l;
  98. int mid=(l+r)/;
  99. if(x<=mid)
  100. return tree[k].l+askl(k*,x);
  101. else return tree[k].l+askl(k*+,x);
  102. }
  103. inline int askr(int k,int x)
  104. {
  105. int l=tree[k].left;
  106. int r=tree[k].right;
  107. if(l==r)
  108. return tree[k].r;
  109. int mid=(l+r)/;
  110. if(x<=mid)
  111. return tree[k].r+askr(k*,x);
  112. else return tree[k].r+askr(k*+,x);
  113. }
  114. int main()
  115. {
  116. n=read();
  117. m=read();
  118. int tot=;
  119. buildtree(,n,);
  120. for(int i=;i<=m;i++)
  121. {
  122. int t,a,b;
  123. cin>>t>>a>>b;
  124. if(t==)
  125. {
  126. insertl(,a-,);
  127. insertr(b+,n,);
  128. tot++;
  129. }
  130. else
  131. {
  132. int ans=askr(,a)+askl(,b);
  133. write(tot-ans);
  134. cout<<endl;
  135. }
  136. }
  137. return ;
  138. }

第二种写法:树状数组

做法:这题是一条条线段,所以我们可以用线段树之类的东东来实现,然后感觉树状数组写起来简单一点所以就打了
开两个数组来存一个是开始的点的数量,一个是结束的 ,然后随便搞一下,最后输出就可以了

下面给出树状数组写法:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N=;
  4. int l[N],r[N];
  5. int n,m;
  6. inline int read()
  7. {
  8. int x=,f=;
  9. char ch=getchar();
  10. while(ch<''||ch>'')
  11. {
  12. if(ch=='-')
  13. f=-;
  14. ch=getchar();
  15. }
  16. while(ch>=''&&ch<='')
  17. {
  18. x=x*+ch-'';
  19. ch=getchar();
  20. }
  21. return x*f;
  22. }
  23. inline void write(int x)
  24. {
  25. if(x<)
  26. {
  27. putchar('-');
  28. x=-x;
  29. }
  30. if(x>)
  31. {
  32. write(x/);
  33. }
  34. putchar(x%+'');
  35. }
  36. int lowbit(int x)
  37. {
  38. return x&-x;
  39. }
  40. void add(int x,int d,int c[])
  41. {
  42. while(x<=n)
  43. {
  44. c[x]+=d;
  45. x+=lowbit(x);
  46. }
  47. }
  48. int sum(int x,int c[])
  49. {
  50. int s=;
  51. while(x>)
  52. {
  53. s+=c[x];
  54. x-=lowbit(x);
  55. }
  56. return s;
  57. }
  58. int main()
  59. {
  60. int k,x,y;
  61. n=read();
  62. m=read();
  63. for(int i=;i<=m;i++)
  64. {
  65. cin>>k>>x>>y;
  66. if(k==)
  67. {
  68. add(x,,l);
  69. add(y,,r);
  70. }
  71. else
  72. {
  73. write(sum(y,l)-sum(x-,r));
  74. cout<<endl;
  75. }
  76. }
  77. return ;
  78. }

第三种方法:括号序列法【简称括号法】

假设有一个长度为10的数轴,我们要将区间[ 2 , 5 ]中种树,这时,我们将 2 处放一个左括号 " ( "  ,5处放一个 " )"  ,表示区间 [ 2 , 5 ]种了树。
查询某个区间树的种类,如区间[ 3 , 10],只需统计10之前(包括10)有多少个‘(’,统计3之前有多少个‘)’,(不包括3)。  
如下图所示:

以上就是括号序列的过程。简单的说,就是更新区间[a,b]时,点a记录左括号数,点b记录右括号数,查询区间[a,b]时,即为b之前(包括b)的左括号数-a之前的右括号数。
下面给出非常简练优秀的代码:
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N=;
  4. int l[N],r[N];
  5. int n,m;
  6. inline int read()
  7. {
  8. int x=,f=;
  9. char ch=getchar();
  10. while(ch<''||ch>'')
  11. {
  12. if(ch=='-')
  13. f=-;
  14. ch=getchar();
  15. }
  16. while(ch>=''&&ch<='')
  17. {
  18. x=x*+ch-'';
  19. ch=getchar();
  20. }
  21. return x*f;
  22. }
  23. inline void write(int x)
  24. {
  25. if(x<)
  26. {
  27. putchar('-');
  28. x=-x;
  29. }
  30. if(x>)
  31. {
  32. write(x/);
  33. }
  34. putchar(x%+'');
  35. }
  36. int main()
  37. {
  38. int k,x,y;
  39. n=read();
  40. m=read();
  41. for(int i=;i<=m;i++)
  42. {
  43. cin>>k>>x>>y;
  44. if(k==)
  45. {
  46. for(int j=x;j<=n;j+=j&-j)
  47. l[j]++;
  48. for(int j=y;j<=n;j+=j&-j)
  49. r[j]++;
  50. }
  51. else
  52. {
  53. int ans=;
  54. for(int j=y;j;j-=j&-j)
  55. ans+=l[j];
  56. for(int j=x-;j;j-=j&-j)
  57. ans-=r[j];
  58. write(ans);
  59. cout<<endl;
  60. }
  61. }
  62. return ;
  63. }

Vijos P1448 校门外的树【多解,线段树,树状数组,括号序列法+暴力优化】的更多相关文章

  1. vijos P1448 校门外的树

    描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的--如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:\(K=1\),读入\(l, ...

  2. 【vijos】P1448 校门外的树

    [题意]两种操作,[L,R]种新的树(不覆盖原来的),或查询[L,R]树的种类数.n<=50000. [算法]树状数组||线段树 [题解]这题可以用主席树实现……不过因为不覆盖原来的,所以有更简 ...

  3. Vijos P1103 校门外的树【线段树,模拟】

    校门外的树 描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……, ...

  4. vijos 1448 校门外的树 树状数组

    描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:K=1,K=1,读入l.r表 ...

  5. vijos 1448 校门外的树 (不是05年普及组那题)

    描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:K=1,K=1,读入l.r表 ...

  6. Ping pong(树状数组求序列中比某个位置上的数小的数字个数)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2492 Ping pong Time Limit: 2000/1000 MS (Java/Others) ...

  7. spoj227 树状数组插队序列问题

    插队问题和线段树解决的方式一样,每个结点维护值的信息是该节点之前的空位有多少,然后从后往前插点即可 注意该题要求输出的是从左往右输出每个士兵的等级,即问士兵最后排在第几个位置 /* 树状数组维护前i个 ...

  8. POJ--3321 Apple Tree(树状数组+dfs(序列))

    Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 22613 Accepted: 6875 Descripti ...

  9. 【NOI P模拟赛】校门外歪脖树上的鸽子(树链剖分)

    题面 2 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 , 1 ≤ l ≤ r ≤ n , 1 ≤ d ≤ 1 0 8 2 ≤ n ≤ 2 × 10^5,1 ≤ m ≤ 2 ...

随机推荐

  1. Spring框架中ModelAndView、Model、ModelMap区别

    原文地址:http://www.cnblogs.com/google4y/p/3421017.html SPRING框架中ModelAndView.Model.ModelMap区别   注意:如果方法 ...

  2. iOS框架搭建(MVC,自定义TabBar)--微博搭建为例

    项目搭建 1.新建一个微博的项目,去掉屏幕旋转 2.设置屏幕方向-->只有竖向 3.使用代码构建UI,不使用storyboard 4.配置图标AppIcon和LaunchImage 将微博资料的 ...

  3. 链表创建和链表遍历算法的演示_C语言

    今天搞了一个多小时,头是疼的,应该是没休息好吧,学习了数据结构这一节,感觉收益良多,下面贴上代码和心得: /*24_链表创建和链表遍历算法的演示*/ # include <stdio.h> ...

  4. android中Log类的封装

    1.为了方便的使用Log打印日志,以及后续方便撤销日志打印,所以对Log类进行封装是一件好事. package market.phone; import android.util.Log; /** * ...

  5. Visual Representation of SQL Joins

    原文:http://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins   从视图上介绍了7种不同类型的JOIN ...

  6. 删除redis 失效节点

    cluster forget命令 cluster forget nodeid

  7. Python学习_06_文件、IO

    文件对象 python中的文件操作和c语言比较类似,包括一些缓冲.偏移量的方式. 文件对象可以通过open().file()两个内建方法创建,两个方法并没有什么不同,使用方法和c语言中的fopen() ...

  8. iOS学习——UI相关小结

    1     StoryBoard: 在Info.plist中可以查看Main storyboard,即入口storyboard,默认为main.storyboard,可以修改为自己创建的storybo ...

  9. UWP 手绘视频创作工具技术分享系列 - Ink & Surface Dial

    本篇作为技术分享系列的第四篇,详细讲一下手绘视频中 Surface Pen 和 Surface Dial 的使用场景. 先放一张微软官方商城的图,Surface 的使用中结合了 Surface Pen ...

  10. AVFoundation 框架初探究(一)

    夜深时动笔 前面一篇文章写了视频播放的几种基本的方式,算是给这个系列开了一个头,这里面最想说和探究的就是AVFoundation框架,很想把这个框架不敢说是完全理解,但至少想把它弄明白它里面到底有什么 ...