题解 P6544 [CEOI2014] Cake
洛谷。
题意
一共 $n$ 个蛋糕,每个蛋糕用有一个权值 $d_i$,会从某一个开始,吃两端权值更小的那个蛋糕。
有 $q$ 个操作,分为两种:
- 修改,将编号为 $i$ 的蛋糕升为第 $e$ 美味的,保证一定是提高排名。
-
查询,询问在吃掉编号为 $b$ 的蛋糕前会吃掉多少蛋糕。
$e\le 10$,$n\le 2.5 \times 10^5$,$q\le 5 \times 10^5$。
先划一下重点:$e\le 10$,并且是提升至 $e$ 这个排名。
分析
重新分析一下题目让我们做一些什么事,我们需要从我们的起始点开始,直到吃到指定点,再吃到指定点的过程中,我们必然吃到从指定点到起始点这一整块上的所有店,那也就代表,要么我们一边吃到了底,否则,就是在那一段有一个比我们这个路径都要大的点。
简单来说,我们的另一端是一个大于我们起始点至结束点最大值的点。
这是我们的查询操作,很明显满足二分性质,只要我们运用前缀和或者区间查询,就可以以 $O(\log n )$ 的时间复杂度解决。
由于起始点与终点的位置关系,因此要分类,此处只贴一边,另一边相近。
inline int queryl(int id) {
int x=tree.query(1,1,n,id,be-1),l=be+1,r=n,res=n+1;
while(l<=r) {
int mid=l+r>>1;
int num=tree.query(1,1,n,be+1,mid);
if(num>x) {
res=mid;
r=mid-1;
} else l=mid+1;
}
return res;
}
接下来,我们的重点就到了修改上,这个修改很不同寻常,一般的修改应该是修改一个值,而此处却是修改到一个排名。
暴力
为了方便表述,坐标为 $id$。
我们的 $d_i$ 两两不同并且 $d_i\le n$,因此,我们的 $d$ 就是一个排列,那么我们只需要将所有值在 $d_{id}$ 与第 $e$ 个之间的 $d$ 下降,然后再将 $id$ 顶上即可。
时间复杂度 $O(n)$,配合暴力的查询可以拿下 15,配合 $O(\log n)$ 的查询可以再拿下 20。
int id=read(),x=read();
for(int j=1;j<=n;++j) if(a[j]>=x&&a[j]<a[id]) ++a[j];
a[id]=x;
tree.build(1,1,n);
正解
我们观察一下我们的查询操作,我们并不需要值的大小,而是需要值的大小关系,我们需要的序列并不一定是一个排列,只要大小关系,排名不变即可。
我们再观察一下我们这个非常优秀的性质 $e\le 10$,这个性质题目的一个约束条件。
我们修改了比我们要修改的更小的值,那我们是不是也可以修改更大的值,而且非常巧啊,我们的这些更大值只要 10 个呢。
我们讲前十大的记录,在修改时,将小于 $e$ 的全部 +1,修改当前点的值,重新维护前十小即可。
int id=read(),x=read();
int num=b[x].num-1;
for(int i=1; i<x; ++i) tree.change(1,1,n,b[i].id,b[i].num-1),b[i].num--;
tree.change(1,1,n,id,num);
int flag=cnt+1;
for(int i=1; i<=cnt; ++i) if(b[i].id==id) flag=i;
b[flag]=<%num,id%>;
if(flag<cnt+1) sort(b+1,b+cnt+1);
else sort(b+1,b+cnt+2);

浙公网安备 33010602011771号