CF1690G Count the Trains 题解

可能更好的阅读体验

题目传送门

题目大意

有一些车厢在铁轨上按照一定速度向左移动,从左到右编号 \(1,2,\dots,n\)。每节车厢的最大速度为 \(v_i\)。靠左边的车厢以最快的速度前进,右边的车厢速度不能超过左边车厢的速度。定义一节火车为一些前进速度相等的车厢。
现在有 \(T\) 个事件,每次第 \(x\) 辆车的速度会减小 \(y\),求每次事件发生之后火车的数量。

题目解析

显然我们可以预处理出每节车厢的前进速度,以及火车的数量。
对于每次修改,如果这节车厢的最大速度还是大于等于车厢现在的速度,那么火车节数不变,否则这节火车就会变成两段,同时这节火车右边的一些火车也会被并掉。
我们考虑用数据结构来维护每一节车厢的速度。

方法一:线段树
这种方法比较好想,但是比较难写。
首先不难发现需要维护区间最小值,同时支持找右边第一个小于这辆火车的速度的火车编号,还要支持单点修改。其实就是线段树的传统操作+线段树二分。

方法二:set
显然一节火车是一个区间,把这些区间按照左端点排序插入到一个 set 中,同时记录这节火车的速度。修改的时候直接暴力把所有会被修改的区间删掉,合并,然后插进去。由于每次操作只会最多增加一个区间,并且如果向右查询的区间除了最后一个都会被删除合并,所以复杂度是正确的。

代码(方法二):

void work(){
	n=read(); T=read(); int i; for(i=1;i<=n;i++) v[i]=read(); now.l=1; now.v=v[1]; s=E;
	for(i=2;i<=n;i++) if(v[i]<now.v){ now.r=i-1; s.insert(now); now.v=v[i]; now.l=i; }
	now.r=n; s.insert(now); set<JTZ>::iterator it; while(T--){
		x=read(); y=read(); v[x]-=y;
		now.l=x; now.v=v[x]; it=s.lower_bound(now);
		if(it==s.end()||(*it).l>x) it--;
		if((*it).v<=v[x]){ print(s.size()),pc(' '); continue; }
		if((*it).l<now.l){
			tmp=*it; s.erase(it); if(tmp.l<=x-1) s.insert(mp(tmp.l,x-1,tmp.v));
			s.insert(mp(x,tmp.r,tmp.v)); it=s.lower_bound(now);
		}
		while((*it).v>=now.v){
			now.r=(*it).r; s.erase(it); it=s.lower_bound(now);
			if(it==s.end()){ now.r=n; break; }
		} s.insert(now); print(s.size()),pc(' ');
	} pc('\n'); return;
}
posted @ 2022-07-11 17:58  jiangtaizhe001  阅读(52)  评论(0编辑  收藏  举报