本次报告撰写人:WilliamZH
题目:202303-4,第29次CCFCSP
使用语言:C++
202303-4 星际网络 II
题目描述:
地址分配采用类似 IPv6的十六进制表示法,每 $4$ 位用”: “隔开。地址长度 $n$ 是 $16$ 的倍数,每个地址由 $n$ 位二进制位组成。
三种操作:
1 id l r
:表示用户 $id$ 申请地址在 $l∼r$ 范围内(包含 $l$ 和 $r$)的一段连续地址块。
若申请的地址全部未被分配,则检查通过;若地址中存在已分配给其他用户的地址,则检查失败。
特殊情况:申请地址中没有已分配给其他用户的地址,但含有已分配给该用户的地址。此时认为检查通过,但若申请的地址先前已全部分配给该用户则检查失败。
如果检查通过,则返回YES
,并将申请地址分配给该用户;若不通过,返回NO
,不改变现有地址分配。2 s
:检查地址 $s$ 被分配给了哪个用户。若未被分配,则结果为 $0$。3 l r
:检查 $l∼r$ 范围内的所有地址是否完整分配给了某用户。若是,回答用户编号;若否,回答 $0$。
输入格式:
第一行,$2$ 个正整数 $n,q$。接下来 $q$ 行,每行一个操作,格式如上所述。
输出格式:
输出 $q$ 行,每行一个非负整数或字符串,表示此次操作的结果。
$n≤512,id≤q≤5×10^4$
题解:
线段树。
- 存储数据:
定义一个结构体node
,用来处理每个操作的信息:op
表示操作类型,id
表示用户编号,l,r
表示左边界和右边界。- 对于操作 1:
将左右边界分别存到一个数组里,并计算右边界+1 后的地址,也存到数组里(将地址范围变成一个左闭右开的区间,方便进行地址分配和范围的比较)。 - 对于操作 2:仅读取地址并将其添加到数组。
- 对于操作 3:读取左边界和右边界,并将它们和右边界的下一个地址添加到数组。
- 对于操作 1:
- 处理数据:
最大值和最小值存储地址分配id
的最大值和最小值,和值存储当前地址块已经有多少地址被分配。- 对数组进行排序并删去重复的元素。
- 构造线段树:
build
函数使用递归方式构建线段树。初始化每个节点的属性,将范围分为两半,并在每一半上调用自身以构建子节点。最后,调用pushup
函数更新当前节点的值。build
函数三个参数:id
表示当前节点,L
表示当前节点区间的左边界,R
表示当前节点区间的右边界。首先为当前节点设置左边界l[id]
、右边界r[id]
、最小值mn[id]
和最大值mx[id]
初始值。如果当前节点的左边界大于等于右边界(即区间只有一个元素),则直接返回,当前节点为叶子节点。否则,计算当前区间的中间位置mid
,然后递归地构建左子树和右子树。左子树的索引为id << 1
;右子树的索引为id << 1 | 1
。pushup
函数利用左子节点和右子节点的索引访问对应的子节点的值。然后,通过max
函数更新当前节点的最大值,通过min
函数更新当前节点的最小值,通过加法操作更新当前节点的和值。
- 进行操作 1:
先查询该区间内地址编号的最小值,如果最小值是初始值,说明没有进行分配,直接全部赋值为id
。如果发现最小值不是初始值,查最大值,如果最小值和最大值不同,说明这块地址多人分配,无需操作;如果最小值和最大值相同,那么地址分配给了一个人,然后用区间和来查询是否全部地址都分配给了这个人,如果区间和等于区间长度,说明这段地址空间全部分配给了一个人,否则判断所查询值与待分配编号是否相同。- 先通过
find
函数查询左右边界在数组中的位置。**(使用lower_bound
函数查找,时间复杂度 $O(log\ n)$,若直接遍历查找会超时)** - 查询当前区间地址是否被分配:
judgemin(int id, int L, int R)
函数: 如果当前节点的区间完全包含在目标区间内,则直接返回当前节点的最小值。否则,调用pushdown
函数将延迟标记向下传递,并通过递归调用查询左右子节点的最小值,并返回其中的较小值。pushdown(int id)
: 如果当前节点的延迟标记lazy[id]
不等于预设值,则将节点的子节点进行更新。最后将当前节点的延迟标记重新设置为预设值。
- 如果未被分配,输出
YES
并调用update
函数,将该区间内的节点信息更新。 - 如果只分配给了一个人
- 通过函数
judgesum
判断是否全部分配给了当前id
。
首先,检查当前节点是否完全位于查询区间内,如果是,则直接返回该节点的和值sum[id]
。否则,调用pushdown
函数将当前节点的懒惰标记进行推送,然后计算当前节点的中点mid
。接着,根据mid
和查询区间的关系,递归调用judgesum
函数在左子节点或右子节点上进行查询,并将两者的结果累加到ans
变量中。最后,返回ans
。 - 如果全部分配,则输出
NO
,否则输出YES
并更新信息。
- 通过函数
- 如果分配给其他人,输出
NO
- 先通过
- 进行操作 2:
单点查询。 - 进行操作 3;
与操作 1 类似。
1 |
|