题目链接:计算机软件能力认证考试系统
样例输入:
32 12
1 1 0001:8000 0001:ffff
2 0001:a000
3 0001:c000 0001:ffff
1 2 0000:0000 000f:ffff
2 0000:1000
1 1 0001:8000 0001:8fff
1 2 0000:0000 0000:ffff
2 0000:1000
1 1 0002:8000 0002:ffff
3 0001:8000 0002:ffff
1 1 0001:c000 0003:ffff
3 0001:8000 0002:ffff
样例输出:
YES
1
1
NO
0
NO
YES
2
YES
0
YES
1
分析:这道题目算是一道线段树模板题了,线段树每个地址代表一个点,这个点的值就是这个地址分配给了哪个编号。我们需要维护的信息有最大值和最小值以及当前地址块已经有多少地址被分配(对应的就是区间和)。最大值初始化为-0x3f3f3f3f,最小值初始化为0x3f3f3f3f.
对于操作1:假如我们要分配给编号id的地址区间为[l,r],那么我们先查询一下获得该区间内地址的编号的最大值是多少,如果最大值是-0x3f3f3f3f,说明这块地址还没有进行分配,那么可以直接进行区间修改把这块地址全部赋值为id。如果发现最大值不是-0x3f3f3f3f,说明这块地址已经存在部分被分配的情况,这个时候我们查一下最小值,如果最小值和最大值不相同,说明这块地址不仅是一个人占有,那么我们就无需进行操作,如果最小值和最大值相同,那么这块地址最多分配给了一个人,这个时候我们要检查一下是不是全部地址都分配给了这个人,这个时候就用区间和来查询,如果区间和等于区间长度,那么就说明这段地址空间全部分配给了一个人,直接返回失败就可以了,否则判断一下我们所查询的值与待分配编号是否相同即可,如果相同,那么说明还有部分地址是空着的,那么我们直接把整个区间的值全部赋值为id即可,否则就是无法继续分配。
对于操作2:就对应一个单点查询操作,如果查询出来为空就输出0,否则输出查询出的id即可。
对于操作3:这个是类似于操作1的,因为操作1是在检查是否完整分配给某个用户的基础上决定是否能够继续分配的,所以操作3就是操作1的一个子操作,这里就不再叙述了。
但是需要注意的就是我们需要对所给定的地址区间边界进行离散化,要不然对于512位数字的可能组合我们是不可能全部记录下来的。但是离散化的时候需要注意,比如现在有两个区间[1,7]和[11,15],这个时候我们不仅要加入1,7,11,15这四个数,我们还要把7和11之间的数选择一个加入,为什么呢?因为如果不加入,那么离散化后的结果就是1->1,7->2,11->3,15->4,假如我们现在已经把[1,7]和[11,15]这两个区间分配给了编号1,那么在离散后的结果上就相当于区间[1,2]和区间[3,4]都已经赋值1,那么我们下次假如想把区间[1,15]全部分配给1,按照题意理解我们可以发现这个操作是合法的因为实际上区间[8,10]属于未分配地址空间,这个时候我们可以把这些地址分配给编号1,但是我们在离散化后的线段树上查询发现区间[1,4]均已分配给编号1,那么就会返回分配失败的消息。但如果我们把每个操作区间右端点后面一个数也加进离散化数组,这个时候我们得到的离散化数组就是1->1,7->2,8->3,11->4,15->5,16->6.那么我们把[1,7]和[11,15]这两个区间分配给了编号1就相当于把区间[1,2]和区间[4,5]赋值为1,下次查询区间[1,15]相当于查询区间[1,5],这个时候可以发现还是存在一些空地址的,这样就可以解决这个问题。
细节见代码:
#include
#include
#include
#include
#include