hdu-4614(线段树+二分)

hdu 4614

题目:

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

数据范围

1≤T≤10,
2≤N,M≤50000,
0≤ABN−1,
11≤F≤10000。

题意:给定一个序列0~N-1,初始都为0,支持两种操作:

  1. 给定一个区间[x,y]和数f,如果里面的数为0,则变为1,变化一个数算一次,一共f次,不足f次则省去剩下的次数省去,输出第一次变为1的下标和最后一变为1的下标,一次都没有则输出“Can not put any one.”。
  2. 给定一个区间[x,y],输出区间和,并将区间内每个数置为0。

分析:
第二个操作挺好做的,只需在标准线段树的query()函数增加一个标记写法,并将结点的sum置为0。
来分析第一个操作,我们可以用二分来写,从第A个位置开始如果这个位置花瓶上为空,我们就找到他后面第一个不为空的位置,更新答案;否则如果这个位置上不为空,我们就找他后面第一个为空的位置,跳到那个位置继续上述过程,直到花插完,或到了最后一个花瓶,输出答案。这个找的过程就可以用二分实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<algorithm>
#include<fstream>
#include<iostream>
#include<cstdio>
#include<deque>
#include<string>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<unordered_map>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 310000
#define N 50010
#define M 10007
#define endl '\n'
#define exp 1e-8
#define lc p << 1
#define rc p << 1|1
#define lowbit(x) ((x)&-(x))
const double pi = acos(-1.0);
typedef long long LL;
typedef unsigned long long ULL;
inline ULL read() {
	ULL x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	return x * f;
}
void print(ULL x) {
	if (x > 9)print(x / 10);
	putchar(x % 10 ^ 48);
}
struct tree
{
	int l, r, sum,add;
}t[4*N];
int cnt, n, m;;
void pushup(int p)
{
	t[p].sum = t[lc].sum + t[rc].sum;
}
void pushdown(int p)
{
	if (t[p].add==1)  //标记为1表示要将区间置每个数为0
	{
		t[lc].sum = 0;
		t[rc].sum = 0;
		t[lc].add = 1;
		t[rc].add = 1;
		t[p].add = 0;
	}
	else if (t[p].add == 2) //标记为2表示要将区间每个数置为1
	{
		t[lc].sum = t[lc].r - t[lc].l + 1;
		t[rc].sum = t[rc].r - t[rc].l + 1;
		t[lc].add = 2;
		t[rc].add = 2;
		t[p].add = 0;
	}
}
void build(int p, int l, int r)
{
	t[p] = { l,r,0,0 };
	if (l == r) return;
	int m = l + r >> 1;
	build(lc, l, m);
	build(rc, m + 1, r);
	pushup(p);
}
void update(int p, int x, int y)  //将区间的数都置为1
{
	if (x <= t[p].l && t[p].r <= y)
	{
		t[p].sum = t[p].r - t[p].l + 1;
		t[p].add = 2;
		return;
	}
	pushdown(p);
	int m = t[p].l + t[p].r >> 1;
	if (x <= m) update(lc, x, y);
	if (y > m)update(rc, x, y);
	pushup(p);
}
int query(int p, int x, int y,int c)  //求区间和,c==1表示还要将区间每个数置为0
{
	if (x <= t[p].l && t[p].r <= y)
	{
		int a = t[p].sum;
		if (c)
		{
			t[p].sum = 0;
			t[p].add = 1;
		}
		return a;
	}
	pushdown(p);
	int sum = 0;
	int m = t[p].l + t[p].r >> 1;
	if (x <= m) sum += query(lc, x, y,c);
	if (y > m) sum += query(rc, x, y,c);
	pushup(p);
	return sum;
}
void insert(int a, int f)
{
	int fi = 0,last = 0;
	bool flag = 0;
	int l = 0, r =0;
	for (int i = a; i <= n&&f!=0; i++)
	{
		l = i - 1, r = n+1;
		while (l != r - 1)  //二分查找从i开始往后第一个有花的位置
		{
			int m = l + r >> 1;
			if (query(1,i, m,0) == 0)  //如果成立,说明i ~ m这段区间都是无花的,更新l = m
				l = m;
			else
				r = m;
		}
		if (l < i) //表示在第i个花瓶处有花,此时需要跳到i后面第一个无花的位置(没有这步会tle)
		{
			l = i - 1, r = n + 1;
			while (l != r - 1)
			{
				int m = l + r >> 1;
				if (query(1, i, m, 0) == m - i + 1)
					l = m;
				else
					r = m;
			}
			i = l;
		}
		else  //如果第i个花瓶处无花,更新答案
		{
			if (!flag)  //表示第一次插入
			{
				flag = 1;
				fi = i;
				if (f > l - i + 1)
				{
					f -= (l - i + 1);
					last = l;
					i = l;        //i= l 是要写的,反则答案会错,也会tle
				}
				else
				{
					last = f + i - 1;
					f = 0;
				}
			}
			else
			{
				if (f > l - i + 1)
				{
					f -= (l - i + 1);
					last = l;
					i = l;
				}
				else
				{
					last = f + i - 1;
					f = 0;
				}
			}
		}
	}
	if (flag)  //如果不为0说明有答案
	{
		update(1, fi, last);
		printf("%d %d\n", fi-1, last-1);
	}
	else
	{
		puts("Can not put any one.");
	}
}
int main()
{
	scanf("%d", &cnt);
	while (cnt--)
	{
		scanf("%d%d", &n,&m);
		build(1, 1, n);
		while (m--)
		{
			int a, b, c;
			scanf("%d%d%d", &a, &b, &c);
			if (a == 1)
			{
				insert(b+1, c);
			}
			else
			{
				printf("%d\n", query(1,b+1,c+1,1));
			}
		}
		printf("\n");
	}
	return 0;
}
posted @ 2023-04-04 21:31  魏老6  阅读(44)  评论(0编辑  收藏  举报