校门外的树

描述

校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……
如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:
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

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

样例输出1

1
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=500050;
  4 int n,m;
  5 inline int read()
  6 {
  7     int x=0,f=1;
  8     char ch=getchar();
  9     while(ch<'0'||ch>'9')
 10     {
 11         if(ch=='-')
 12             f=-1;
 13         ch=getchar();
 14     }
 15     while(ch>='0'&&ch<='9')
 16     {
 17         x=x*10+ch-'0';
 18         ch=getchar();
 19     }
 20     return x*f;
 21 }
 22 inline void write(int x)
 23 {
 24     if(x<0)
 25     {
 26         putchar('-');
 27         x=-x;
 28     }
 29     if(x>9)
 30     {
 31         write(x/10);
 32     }
 33     putchar(x%10+'0');
 34 }
 35 struct Tree
 36 {
 37     int l,r;
 38     int left,right;
 39 }tree[N<<3];
 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)/2;
 49     buildtree(x,mid,pos*2);
 50     buildtree(mid+1,y,pos*2+1);
 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)/2;
 62     if(y<=mid)
 63         insertl(x,y,pos*2);
 64     else if(x>mid)
 65         insertl(x,y,pos*2+1);
 66     else
 67     {
 68         insertl(x,mid,pos*2);
 69         insertl(mid+1,y,pos*2+1);
 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)/2;
 82     if(y<=mid)
 83         insertr(x,y,pos*2);
 84     else if(x>mid)
 85         insertr(x,y,pos*2+1);
 86     else
 87     {
 88         insertr(x,mid,pos*2);
 89         insertr(mid+1,y,pos*2+1);
 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)/2;
 99     if(x<=mid)
100         return tree[k].l+askl(k*2,x);
101     else return tree[k].l+askl(k*2+1,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)/2;
110     if(x<=mid)
111         return tree[k].r+askr(k*2,x);
112     else return tree[k].r+askr(k*2+1,x);
113 }
114 int main()
115 {
116     n=read();
117     m=read();
118     int tot=0;
119     buildtree(0,n,1);
120     for(int i=1;i<=m;i++)
121     {
122         int t,a,b;
123         cin>>t>>a>>b;
124         if(t==1)
125         {
126             insertl(0,a-1,1);
127             insertr(b+1,n,1);
128             tot++;
129         }
130         else
131         {
132             int ans=askr(1,a)+askl(1,b);
133             write(tot-ans);
134             cout<<endl;
135         }
136     }
137     return 0;
138 }

第二种写法:树状数组

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

下面给出树状数组写法:

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

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

  1. 【vijos】P1448 校门外的树

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

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

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

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

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

  4. 机器学习算法(二十五):KD树详解及KD树最近邻算法

    目录 1 KD树 1.1 什么是KD树 1.2 KD树的构建 1.3 KD树的插入 1.4 KD树的删除 1.5 KD树的最近邻搜索算法 1.5.1 举例:查询点(2.1,3.1) 1.5.2 举例: ...

  5. 奇小葩讲设备树(5/5)-- Linux设备树详解(五)设备树的使用

    对于任何的知识来说,了解了理论的知识,知道了设备树怎么解析用以代替传统的范式之后,我们需要知道怎么使用设备树.对于使用我们分两部分,一部分是它有哪些接口,能做些什么,至于怎么编写dts文件本章不讨论. ...

  6. KD树详解及KD树最近邻算法

    参考:http://blog.csdn.net/app_12062011/article/details/51986805 http://www.cnblogs.com/snake-hand/arch ...

  7. 【树】B032_LC_ 二叉树中的伪回文路径(暴力 / 优化)

    一.Problem Given a binary tree where node values are digits from 1 to 9. A path in the binary tree is ...

  8. Loj 10115 「一本通 4.1 例 3」校门外的树 (树状数组)

    题目链接:https://loj.ac/problem/10115 题目描述 原题来自:Vijos P1448 校门外有很多树,学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的 ...

  9. 「一本通 4.1 例 3」校门外的树 (loj10115)

    题目描述 原题来自:Vijos P1448 校门外有很多树,学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两种操作: K=1,读入 l,r表示在 l 到 r 之间种上 ...

最新文章

  1. j函数 判断以 什么开头
  2. 【杂谈】如何在专家指导下系统性学习自然语言处理
  3. 岗位内推 | 微软亚洲互联网工程院自然语言处理组招聘
  4. 厉害了,在Pandas中用SQL来查询数据,效率超高
  5. Consul-template+nginx实现自动负载均衡
  6. nginx fastcgi python_webpy + nginx + fastcgi 构建python应用
  7. 安卓手机卡顿怎么解决_手机卡顿怎么办? 几招教你轻松解决!
  8. node.js与python_Node.js与Python
  9. 三菱plc pwm指令_常用PLC各系列简介大全,选型必看!
  10. Ubuntu20.04 安装向日葵SunloginClient并解决报错缺少依赖问题
  11. 如何安装老版本Eclipse汉化——以2020-06为例
  12. 计算机组装维修中级试题,维修电工中级培训考试题及答案
  13. element技巧之element的dialog弹出框可拖拽、可拉伸、可全屏并处理边界问题
  14. 垃圾邮件服务器 查询,邮件服务器ip黑名单查询
  15. 机器学习——算法介绍-4
  16. TestCenter Layer4-7分析
  17. 深入理解char * ,char ** ,char a[ ] ,char *a[]
  18. html 带箭头的提示框,css实现对话框-带箭头提示框
  19. 进入外包公司之后…………
  20. 最短路(两种常用算法!!!)

热门文章

  1. ubuntu下rar文件解压后文件名乱码的解决方案
  2. Waymo也商业化了!“早期乘客”项目开始测试收费,凤凰城人民掏了腰包
  3. Javascript知识汇总------获取构造函数constructor名称和一些字符串处理方法
  4. Gym - 101194F(后缀数组)
  5. Problem 69:Totient maximum
  6. [剑指Offer]9.用两个栈实现队列
  7. 【命令小结】“|”的用法
  8. linux下图形远程桌面
  9. Linux下限制用户通过SFTP访问指定目录
  10. 通过telnet自动下载cfg配置文件