题目大意:
给你N个花瓶,编号是0 到 N - 1 ,初始状态花瓶是空的,每个花瓶最多插一朵花
两种操作:
操作1:a b 往在a位置后面(包括a)插b朵花,输出插入的首位置和末位置。
操作2:a b 输出区间【a,b】之间的花的数量,然后将这个区间内的花瓶清空
解题思路:
这是多校第二场的一道题目,这一道题卡到最后我们也没有做出来,比赛后搞了一下,顺便复习了一下线段树
线段树的区间更新、区间查找,时间复杂度为logN
这道题的难点在于巧妙的运用二分来寻找区间的结束位置
#include<iostream>
#include<cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int N = 50010;
const int INF = 1 << 30;
int sum[N<<2]; //记录占用区间的长度
int flag[N<<2]; //延迟标记
int len[N<<2]; //记录空闲区间的长度
int addr[N<<2]; //记录区间内第一个为空的位置
int n,q;
void Pushup(int rt,int l,int r) //向上更新
{
int ls = rt << 1;
int rs = rt << 1|1;
sum[rt] = sum[ls] + sum[rs];
len[rt] = len[ls] + len[rs];
addr[rt] = min(addr[ls],addr[rs]);
return;
}
void Pushdown(int rt,int l,int r) //向下更新延迟标记
{
int m = (l + r) >> 1;
int ls = rt << 1;
int rs = rt << 1|1;
if(flag[rt] != -1)
{
flag[ls] = flag[rs] = flag[rt];
if(flag[rt] == 1)
{
sum[ls] = m - l + 1;
sum[rs] = r - m;
len[rs] = len[ls] = 0;
addr[ls] = addr[rs] = INF;
}
else
{
sum[ls] = sum[rs] = 0;
len[ls] = m - l + 1;
len[rs] = r - m;
addr[ls] = l;
addr[rs] = m + 1;
}
flag[rt] = -1;
}
return;
}
void build(int l,int r,int rt)//建树
{
flag[rt] = -1;
if(l == r)
{
sum[rt] = 0;
len[rt] = r - l + 1;
addr[rt] = l;
return;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
Pushup(rt,l,r);
return;
}
int QuerySum(int L,int R,int l,int r,int rt) //统计区间内非空的位置数
{
if(L <= l && r <= R) return sum[rt];
Pushdown(rt,l,r);
int m = (l + r) >> 1;
int res = 0;
if(L <= m) res += QuerySum(L,R,lson);
if(R > m) res += QuerySum(L,R,rson);
Pushup(rt,l,r);
return res;
}
int QueryPos(int L,int R,int l,int r,int rt)//查找区间内第一个非空的位置
{
if(L <= l && r <= R)
{
return addr[rt];
}
Pushdown(rt,l,r);
int m = (l + r) >> 1;
int tmp = INF;
if(L <= m) tmp = min(tmp,QueryPos(L,R,lson));
if(R > m) tmp = min(tmp,QueryPos(L,R,rson));
Pushup(rt,l,r);
return tmp;
}
void Update(int L, int R, int c, int l, int r, int rt) //更新区间
{
if(L <= l && r <= R)
{
flag[rt] = c;
if(flag[rt] == 1) //延迟标记
{
sum[rt] = r - l + 1;
len[rt] = 0;
addr[rt] = INF;
}
else
{
sum[rt] = 0;
len[rt] = r - l + 1;
addr[rt] = l;
}
return;
}
Pushdown(rt,l,r);
int m = (l + r) >> 1;
if(L <= m) Update(L,R,c,lson);
if(R > m) Update(L,R,c, rson);
Pushup(rt,l,r);
return;
}
int binary_search(int l, int r, int st, int tar)//二分查找
{
while(l <= r)
{
int mid = (l + r) >> 1;
int sum = QuerySum(st, mid, 0, n - 1, 1);
sum = mid - st +1 -sum;
if(sum >= tar) r = mid;
else l = mid + 1;
if(l == r) return l;
}
return l;
}
int main()
{
//freopen("Input.txt","r",stdin);
int T;
int k,a,b,i;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&q);
build(0,n-1,1); //构建线段树
for(i = 0; i < q; i++)
{
scanf("%d%d%d",&k,&a,&b);
if(k == 1) //第一种操作
{
if(b == 0) continue;
int ans = QuerySum(a, n-1, 0, n-1, 1); //查询从a到末尾的区间内有多少非空的位置
if(ans == n-a) //不存在非空的位置
puts("Can not put any one.");
else
{
int p = min(n - a - ans, b); //可以插的花的数量
int left = QueryPos(a, n-1, 0, n-1, 1); //查找区间内第一个空位
int right = binary_search(a, n-1, a, p);//二分查找末尾的位置
printf("%d %d\n",left,right);
Update(left, right, 1, 0, n-1, 1); //更新区间内的值
}
}
else
{
int ans = QuerySum(a, b, 0, n-1, 1);
printf("%d\n",ans);
Update(a, b, 0, 0, n-1, 1); //更新区间内的值
}
}
puts("");
}
return 0;
}