ases and Flowers HDU - 4614 区间更新 + 二分查找 || 区间更新 + 第k个值判断

一、内容

 Alice is so popular that she can receive many flowers everyday. She has N vases numbered from 0 to N-1. When she receive some flowers, she will try to put them in the vases, one flower in one vase. She randomly choose the vase A and try to put a flower in the vase. If the there is no flower in the vase, she will put a flower in it, otherwise she skip this vase. And then she will try put in the vase A+1, A+2, ..., N-1, until there is no flower left or she has tried the vase N-1. The left flowers will be discarded. Of course, sometimes she will clean the vases. Because there are too many vases, she randomly choose to clean the vases numbered from A to B(A <= B). The flowers in the cleaned vases will be discarded. 

Input

  The first line contains an integer T, indicating the number of test cases.
  For each test case, the first line contains two integers N(1 < N < 50001) and M(1 < M < 50001). N is the number of vases, and M is the operations of Alice. Each of the next M lines contains three integers. The first integer of one line is K(1 or 2). If K is 1, then two integers A and F follow. It means Alice receive F flowers and try to put a flower in the vase A first. If K is 2, then two integers A and B follow. It means the owner would like to clean the vases numbered from A to B(A <= B).

Output

  For each operation of which K is 1, output the position of the vase in which Alice put the first flower and last one, separated by a blank. If she can not put any one, then output 'Can not put any one.'. For each operation of which K is 2, output the number of discarded flowers.
   Output one blank line after each test case.

Sample Input

2
10 5
1 3 5
2 4 5
1 1 8
2 3 6
1 8 8
10 6
1 2 5
2 3 4
1 0 8
2 2 5
1 4 4
1 2 3

Sample Output

3 7
2
1 9
4
Can not put any one.

2 6
2
0 9
4
4 5
2 3

二、思路

  • 第一种思路:每次查询某个区间【X, N】这个区间是否能放一朵花,若能放就返回最后一朵花的位置。若不能放则返回-1。 每次线段树向下递归的时候,判断一下左边空花瓶的数量是否>=f, 若大于代表左边区间的花瓶就可以放完,那么直接递归左边区间,反之则将花的数量-左边区间空花瓶的数量,然后递归右区间。类似主席树求第K大的思想。最后到根节点再判断一下该节点是否能够放花,若不能那么只能又去找另一区间。比如递归了右区间,但是发现右边区间已经无法放了,那么只能去左边区间找最后一朵花的位置。
  • 第二种思路: 二分查找【x,mid】 中空花瓶的数量,若大于等于f(这里的f = min([x,n]中空花瓶的数量,f) )代表答案在左边区间。否则答案在右边区间。h

三、代码

较快方法:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 5e4 + 5;
struct Node {
	int sum, lazy; //sum里面保存当前空花瓶的数量  lazy代表当前的状态 0是满的 1是空的 用于标记下放 
} tr[N << 2];
int n, m, t, k, x, y, f;  
void pushup(int id) {
	tr[id].sum = tr[id << 1].sum + tr[id << 1 | 1].sum; 
}
void pushdown(int id, int l, int r) {
	if (tr[id].lazy == -1) return;
	if (tr[id].lazy == 0) {
		tr[id << 1 | 1].sum = tr[id << 1].sum = 0;
		tr[id << 1].lazy = tr[id << 1 | 1].lazy = 0;
	} else if (tr[id].lazy == 1) {
		int mid = (l + r) >> 1;
		tr[id << 1].sum = mid - l + 1;
		tr[id << 1 | 1].sum = r - mid;
		tr[id << 1].lazy = tr[id << 1 | 1].lazy = 1;
	}
	tr[id].lazy = -1;
}
void build(int id, int l, int r) {
	if (l == r) {
		tr[id].sum = 1; //代表花瓶为空
		tr[id].lazy = -1;
		return;
	}
	int mid =  (l + r) >> 1;
	build(id << 1, l, mid); 
	build(id << 1 | 1, mid + 1, r);
	tr[id].lazy = -1;
	pushup(id); 
}
void update(int id, int l, int r, int x, int y, int d) {
	if (x <= l && r <= y) {
		if (d == 1) tr[id].sum = r - l + 1;
		else tr[id].sum = 0;
		tr[id].lazy = d;
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(id, l, r);
	if (x <= mid) update(id << 1, l, mid, x, y, d);
	if (y > mid) update(id << 1 | 1, mid + 1, r, x, y, d);
	pushup(id);
}
//查询【x,y】区间空花瓶的数量 
int query(int id, int l, int r, int x, int y) {
	if (x <= l && r <= y) return tr[id].sum;
	int mid = (l + r) >> 1;
	pushdown(id, l, r);
	int ans = 0;
	if (x <= mid) ans += query(id << 1, l, mid, x, y);
	if (y > mid) ans += query(id << 1 | 1, mid + 1, r, x, y);
 	return ans; 
}
//看是否能放花 若能放一朵以上返回最后一朵的位置 若一朵都不能放就返回-1 
int query(int id, int l, int r, int x, int y, int k) {
	if (l == r) {
		//可能最后一朵花也没插
		if (k <= f  && tr[id].sum == 0) return -1; //不能返回当前位置 
		if (k <= f && tr[id].sum == 1) return l;
	}
	pushdown(id, l, r);
	int mid = (l + r) >> 1;
	int left = 0, right = 0, ans = -1;
	if (x <= mid) left = query(id << 1, l, mid, x, mid); // 查询左边区间在【x, mid】 
	if (y > mid) right = query(id << 1 | 1, mid + 1, r, mid + 1, y);
	if (k > left) ans = query(id << 1 | 1, mid + 1, r, x, y, k - left);
	else ans = query(id << 1, l, mid, x, y, k);//查询左边区间 
	if (ans == -1) {
		//代表要么一朵花也插不进去或者是位置不符合
		if (left == 0) return -1;//代表一朵花也插入不辽
		ans = query(id << 1, l, mid, x, y, k); 
	}
	return ans;
}
int main() {
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d", &n, &m);
		build(1, 1, n);
		for (int i = 1; i <= m; i++) {
			scanf("%d%d%d", &k, &x, &y);
			if (k == 1) {
				x++; f = y;//默认1-n 
				//查询x-n 
				int e = query(1, 1, n, x, n, f); 
				if (e == -1) printf("Can not put any one.\n"); 
				else  {
					//查询一下第一朵花的位置	
					int s = query(1, 1, n, x, n, 1);
					printf("%d %d\n", s - 1, e - 1);
					update(1, 1, n, s, e, 0); // 1代表花瓶空的 0代表花瓶满了  
				}
			} else {
				x++, y++; 
				int t = query(1, 1, n, x, y); //查询某段区间的空花瓶数量
				update(1, 1, n, x, y, 1); 
				printf("%d\n", y - x + 1 - t); 
			}
		}
		printf("\n");
	} 
	return 0;
} 

二分查找:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5e4 + 5;
struct Node {
	int lazy, sum; //lazy用于区间修改的标记 sum记录空花瓶的数量 1代表空  0代表装满 
} tr[N << 2];
int t, n, m, x, y, k;
void pushdown(int id, int l, int r) {
	if (tr[id].lazy == -1) return;
	int mid = (l + r) >> 1;
	tr[id << 1].sum = (mid - l + 1) * tr[id].lazy;
	tr[id << 1 | 1].sum = (r - mid) * tr[id].lazy;
	tr[id << 1].lazy = tr[id << 1 | 1].lazy = tr[id].lazy;
	tr[id].lazy = -1;
} 
void pushup(int id) { 
	tr[id].sum = tr[id << 1].sum + tr[id << 1 | 1].sum; 
}
void build(int id, int l, int r) {
	if (l == r) {
		tr[id].sum = 1;
		tr[id].lazy = -1;
		return;
	}
	int mid = (l + r) >> 1;
	build(id << 1, l, mid);
	build(id << 1 | 1, mid + 1, r);
	tr[id].lazy = -1;
	pushup(id);
}
int query(int id, int l, int r, int x, int y) {
	if (x <= l && r <= y) return tr[id].sum;
	int ans = 0;
	pushdown(id, l , r);
	int mid = (l + r) >> 1;
	if (x <= mid) ans += query(id << 1, l, mid, x, y);
	if (y > mid) ans += query(id << 1 | 1, mid + 1, r, x, y);
	return ans; 
}
void update(int id, int l, int r , int x, int y, int d) {
	if (x <= l && r <= y) {
		tr[id].sum = (r - l + 1) * d;
		tr[id].lazy = d;
		return ;
	}
	pushdown(id, l, r);
	int mid = (l + r) >> 1;
	if (x <= mid) update(id << 1, l, mid, x, y, d);
	if (y > mid) update(id << 1 | 1, mid + 1, r, x, y, d);
	pushup(id);
}
int binarysearch(int x, int n, int f) {
	int l = x, r = n;
	while (l < r) {
		int mid = (l + r) >> 1;
		int t = query(1, 1, n, x, mid);
		if (t >= f) { 
			r = mid; //代表答案在左边区间 
		} else {
			l = mid + 1;
		}
	}
	return r;
}
int main() {
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d", &n, &m);
		build(1, 1, n);
		for (int i = 1; i <= m; i++) {
			scanf("%d%d%d", &k, &x, &y);
			if (k == 1) {
				x++; //默认下标为【1, n】 
				//先查询下【x, n】中空花瓶的数量 看是否符合
				int t = query(1, 1, n, x, n);
				if (t == 0) {
					printf("Can not put any one.\n");
				} else {
					t = min(y, t); // 获取最少能够放的花的数量
					int s = binarysearch(x, n, 1); //二分查找花为1的时候的位置,即开始
					int e = binarysearch(x, n, t); 
					printf("%d %d\n", s - 1, e - 1);
					update(1, 1, n, s, e, 0); //代表花瓶为满的状态 
				}
			} else {
				x++, y++;
				int t =  query(1, 1, n, x, y);
				update(1, 1, n, x, y, 1); //代表花瓶为空 
				printf("%d\n", y - x + 1 - t); //输出x-y中装了花的花瓶数 
			}
 		}
 		printf("\n");
	}
	return 0;
} 
发布了351 篇原创文章 · 获赞 277 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41280600/article/details/104080249