题目大意:
给你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; }