链接:https://vjudge.net/contest/260644#problem/L
思路:插花,每次如果能够完成操作返回第一朵花的位置和最后一朵花的位置,还有清空操作,我们考虑到第一朵花的位置应该搜索到当前区间和恰好等于终点-起点+1的那个区间的左端点,最后一朵花的位置就是左端点向右插x朵花所到达的位置,所以我们需要记录每个区间的和即可,然后通过二分查找找到起始位置,然后二分找到最终位置,最后update完成更新,清空的话就用tag标记正常操作即可。(总结一下,对于这种离散的区间修改问题的另一个思路是,先确定更改整体的最小范围,即最小要把哪段连续的更改,然后update的时候直接整段更改并且传递标记即可)。
代码:
#include
using namespace std;
const int maxn = 1e5+10;
int sum[maxn<<2],tag[maxn<<2];
int t,n,q;
inline void pushup(int o){
sum[o] = sum[o<<1] + sum[o<<1|1];
}
void pushdown(int o,int m){
tag[o<<1] = tag[o<<1|1] = tag[o];
if(tag[o]){
sum[o<<1] = m - (m>>1);
sum[o<<1|1] = m>>1;
}
else sum[o<<1] = sum[o<<1|1] = 0;
tag[o] = -1;
}
void build(int o,int l,int r){
tag[o] = -1;
sum[o] = 0;
if(l>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
}
}
void update(int o,int tl,int tr,int l,int r,int v){
if(tl==l&&tr==r){
tag[o] = v;
if(v)sum[o] = tr-tl+1;
else sum[o] = 0;
return;
}
if(tag[o] > -1)
pushdown(o,tr-tl+1);
int mid = tl+tr>>1;
if(r <= mid)
update(o<<1,tl,mid,l,r,v);
else
{
if(l > mid)
update(o<<1|1,mid + 1,tr,l,r,v);
else
{
update(o<<1,tl,mid,l,mid,v);
update(o<<1|1,mid + 1,tr,mid + 1,r,v);
}
}
pushup(o);
}
int query(int o,int tl,int tr,int l,int r){
if(l==tl&&tr==r)return sum[o];
if(tag[o]>-1)
pushdown(o,tr-tl+1);
int mid = (tl+tr)>>1;
int ret = 0;
if(r<=mid) ret = query(o<<1,tl,mid,l,r);
else{
if(l>mid)ret = query(o<<1|1,mid+1,tr,l,r);
else ret = query(o<<1,tl,mid,l,mid) + query(o<<1|1,mid+1,tr,mid+1,r);
}
return ret;
}
int bs(int s,int len){//二分枚举起点和终点
int l = s;
int r = n;
int ans = -1;
int mid;
while(l<=r){//注意终点不同的二分写法,如果是r>l的写法需要先判断是否存在解,如果无解的话会使二分陷入死循环
int mid = l+r>>1;
int tmp = query(1,1,n,s,mid);
if(tmp+len==mid-s+1){
ans = mid;
r = mid-1;
}
else{
if(tmp+len