题目
题目描述
PP大厦有一间空的礼堂,可以为企业或者单位提供会议场地。这些会议中的大多数都需要连续几天的时间(个别的可能只需要一天),不过场地只有一个,所以不同的会议的时间申请不能够冲突。
也就是说,前一个会议的结束日期必须在后一个会议的开始日期之前。所以,如果要接受一个新的场地预约申请,就必须拒绝掉与这个申请相冲突的预约。
一般来说,如果PP大厦方面事先已经接受了一个会场预约,例如从10日到15日,就不会在接受与之相冲突的预约,例如从12日到17日。
不过,有时出于经济利益,PP大厦方面有时会为了接受一个新的会场预约,而拒绝掉一个甚至几个之前预订的预约。 于是,礼堂管理员QQ的笔记本上笔记本上经常记录着这样的信息: 本题中为方便起见,所有的日期都用一个整数表示。例如,如果一个为期10天的会议从“90日”开始到“99日”,那么下一个会议最早只能在“100日”开始。
最近,这个业务的工作量与日俱增,礼堂的管理员QQ希望参加SHTSC的你替他设计一套计算机系统,方便他的工作。这个系统应当能执行下面两个操作:
A操作:有一个新的预约是从“start日”到“end日”,并且拒绝掉所有与它相冲突的预约。执行这个操作的时候,你的系统应当返回为了这个新预约而拒绝掉的预约个数,以方便QQ与自己的记录相校对。
B操作:请你的系统返回当前的仍然有效的预约的总数。
输入格式
输入文件的第一行是一个整数n,表示你的系统将接受的操作总数。
接下去n行每行表示一个操作。每一行的格式为下面两者之一:
“A start end”表示一个A操作;
“B”表示一个B操作。
输出格式
输出文件有n行,每行一次对应一个输入。表示你的系统对于该操作的返回值。
输入输出样例
6 A 10 15 A 17 19 A 12 17 A 90 99 A 11 12 B
0 0 2 0 1 2
说明/提示
N< = 200000
1< = Start End < = 100000
分析
有五种做法:线段树,树状数组,平衡树,分块,二分
1、线段树
各个预约的时间不能重合,又都是连续的一段区间,不妨将每一个预约看成是染色,操作编号便是色彩编号
放几个写的不错的线段树题解,然鹅我的方法跟他们不太一样,看代码就知道了嘛
【[SHOI2009]会场预约】 by asuldb
P2161 [SHOI2009]会场预约 by 香风智乃
part 1 关于操作
整理题目,发现操作可以分为三种:
(1)查询:查询整个区间的颜色数
(2)修改:修改区间的颜色
(3)消除:消除某个颜色
part 2 关于消除
似乎消除没怎么见过诶……
注意到消除是消除一个颜色(废话)
关于消除,如果想不出在修改的过程中有什么好方法可以直接把这个区间清零的话,
不如直接对这个颜色打个标记(因为这不仅是一个区间,它有颜色,且有标记),修改时判断一下即可
part 3 关于查询
由于只问了整个区间的颜色数,那么直接用一个变量存就好
其实颜色个数也不能直接相加,用线段树反而麻烦
part 4 关于线段树
肯定要存颜色,还要有一个标记看这个区间是否只有一种颜色
只有一种颜色的时候才好操作
我们没存颜色个数,颜色可能会跨区间,统计起来很麻烦。
不如只操作只有一种颜色的区间,颜色的查找与计数都方便
part 5 代码(有细节注解)
1 /************************** 2 User:Mandy.H.Y 3 Language:c++ 4 Problem: 5 Algorithm:线段树 6 Date: 7 **************************/ 8 9 //线段树,染色 10 //题目要支持三种操作: 11 //查询:查询区间颜色数 12 //修改:修改区间颜色 13 //消除:消除区间的颜色 - 标记即可 14 //方便起见,没有颜色也当做一种颜色 15 16 #include<bits/stdc++.h> 17 #define lson l,mid,k << 1 18 #define rson mid + 1,r,k << 1 | 1 19 20 using namespace std; 21 22 const int maxn = 2e5 + 5; 23 24 int ans,color,tag[maxn << 2],n,cnt,m; 25 bool vis[maxn],sum[maxn << 2]; 26 //vis[i] - 颜色i是否存在 27 //sum[i] - i所代表的区间是否只有一种颜色(0 - 只有一种,1 - 有多种) 28 //tag[i] - 区间 i 的颜色 29 30 template<class T>inline void read(T &x){ 31 x = 0;bool flag = 0;char ch = getchar(); 32 while( ! isdigit(ch)) flag |= ch == '-',ch = getchar(); 33 while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); 34 if(flag) x = -x; 35 } 36 37 template<class T>void putch(const T x){ 38 if(x > 9) putch(x / 10); 39 putchar(x % 10 | 48); 40 } 41 42 template<class T>void put(const T x){ 43 if(x < 0) putchar('-'),putch(-x); 44 else putch(x); 45 } 46 47 void file(){ 48 freopen("2161.in","r",stdin); 49 // freopen("2161.out","w",stdout); 50 } 51 52 void readdata(){ 53 read(n); 54 } 55 56 void pushdown(int l,int r,int k){ 57 if(tag[k] && vis[tag[k]]){//有颜色 && 颜色还在 58 int ls = k<<1; 59 int rs = k<<1|1; 60 tag[ls] = tag[k]; 61 tag[rs] = tag[k]; 62 } 63 } 64 65 bool modify(int l,int r,int k,int x,int y){ 66 if(x <= l && r <= y && sum[k] == 0){ 67 if(vis[tag[k]]) ans--,cnt++;//如果颜色还在 68 vis[tag[k]] = 0; 69 tag[k] = color; 70 return 1;//纯色区间 71 } 72 pushdown(l,r,k);//下传颜色 73 int mid = (l + r) >> 1, ls = 1,rs = 1; 74 //都是看这是不是一个杂色区间 75 if(x <= mid) { 76 if(modify(lson,x,y)) sum[k<<1] = 0,ls = 0; 77 //如果是纯区间sum清零,ls标记为零 78 //之所以要ls与rs是因为 sum[k<<1]可能本身就是零,而两个区间的颜色不一样 79 } 80 if(y > mid) { 81 if(modify(rson,x,y)) sum[k<<1|1] = 0,rs = 0; 82 } 83 sum[k] = sum[k<<1] | sum[k<<1|1] | ls | rs;//有一个条件不满足就是杂色区间 84 if(!sum[k]){ 85 tag[k] = tag[k<<1]; 86 //把颜色标记上传,重要 87 //可能由于这一次覆盖了有多个颜色的区间,需要将颜色上传 88 //不然可能sum为0而tag也为0 89 return 1; 90 } 91 else return 0; 92 } 93 94 void work(){ 95 int m = 1e5 + 5; 96 //注意区间大小 97 for(int i = 1;i <= n; ++ i){ 98 99 char c = getchar(); 100 while(c != 'A' && c != 'B') c = getchar(); 101 //注意读入 102 if(c == 'A'){ 103 int ll,rl;color = i; 104 vis[i] = 1;ans++;//加颜色 105 cnt = 0;//清零 106 read(ll);read(rl); 107 modify(1,m,1,ll,rl); 108 put(cnt); 109 puts(""); 110 } else { 111 put(ans); 112 puts(""); 113 } 114 } 115 } 116 117 int main(){ 118 // file(); 119 readdata(); 120 work(); 121 return 0; 122 }
2、树状数组
part 1