HDU 3727 Jewel(划分树 + 二分)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3727

 

题意:有四类操作:

1.insert x 在当前序列最后插入数x

2.query_1 s t k 查询序列[s, t]内第k小的数

3.query_2 x 查询数x的rank

4.query_3 x 查询[1, len]内第k小的数,len为当前序列的长度

 

思路:

因为每次插入数都是在最后,故考虑离线操作,对于1,3操作都可以记录查询范围然后划分树求解,对于2操作可以通过二分该rank + 划分树求值判断

 

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#include <cmath>
#include <queue>
#include <set>
#include <map>
#include <climits>
#include <functional>
#include <deque>
#include <ctime>

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;

const int maxn = 100010;

vector <int> g[maxn];

int vis[maxn], cnt;

int tree[20][maxn];//表示每层每个位置的值
int sorted[maxn];//已经排序好的数
int toleft[20][maxn];//toleft[p][i]表示第i层从1到i有数分入左边

void build(int l, int r, int dep)
{
	if (l == r) return;
	int mid = (l + r) >> 1;
	int same = mid - l + 1;
	for (int i = l; i <= r; i++)
		if (tree[dep][i] < sorted[mid])
			same--;
	int lpos = l;
	int rpos = mid + 1;
	for (int i = l; i <= r; i++)
	{
		if (tree[dep][i] < sorted[mid])
			tree[dep + 1][lpos++] = tree[dep][i];
		else if (tree[dep][i] == sorted[mid] && same > 0)
		{
			tree[dep + 1][lpos++] = tree[dep][i];
			same--;
		}
		else
			tree[dep + 1][rpos++] = tree[dep][i];
		toleft[dep][i] = toleft[dep][l - 1] + lpos - l;
	}
	build(l, mid, dep + 1);
	build(mid + 1, r, dep + 1);
}

//查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
int query(int L, int R, int l, int r, int dep, int k)
{
	if (l == r) return tree[dep][l];
	int mid = (L + R) >> 1;
	int cnt = toleft[dep][r] - toleft[dep][l - 1];
	if (cnt >= k)
	{
		int newl = L + toleft[dep][l - 1] - toleft[dep][L - 1];
		int newr = newl + cnt - 1;
		return query(L, mid, newl, newr, dep + 1, k);
	}
	else
	{
		int newr = r + toleft[dep][R] - toleft[dep][r];
		int newl = newr - (r - l - cnt);
		return query(mid + 1, R, newl, newr, dep + 1, k - cnt);
	}
}

struct node
{
	int kind, l, r, v;
} op[150010];

int id[150010];

int main()
{
	int ca = 1;
	int n;
	while (~scanf("%d", &n))
	{
		printf("Case %d:\n", ca++);
		char s[30];
		int cnt = 1;
		int num = 0;
		memset(tree, 0, sizeof(tree));
		for (int i = 0; i < n; i++)
		{
			scanf("%s", s);
			if (s[0] == 'I')
			{
				int v;
				scanf("%d", &v);
				sorted[cnt] = id[cnt] = tree[0][cnt] = v;
				cnt++;
			}
			else if (s[0] == 'Q' && s[6] == '1')
			{
				scanf("%d%d%d", &op[num].l, &op[num].r, &op[num].v);
				op[num].kind = 1;
				num++;
			}
			else if (s[0] == 'Q' && s[6] == '2')
			{
				scanf("%d", &op[num].v);
				op[num].l = 1, op[num].r = cnt - 1;
				op[num].kind = 2;
				num++;
			}
			else if (s[0] == 'Q' && s[6] == '3')
			{
				scanf("%d", &op[num].v);
				op[num].l = 1, op[num].r = cnt - 1;
				op[num].kind = 3;
				num++;
			}
		}
		cnt--;
		// for(int i = 0; i < num; i++)
		// 	printf("%d %d\n", op[i].kind, op[i].v);

		sort(sorted + 1, sorted + cnt + 1);
		build(1, cnt, 0);

		long long s1 = 0, s2 = 0, s3 = 0;
		for (int i = 0; i < num; i++)
		{
			if (op[i].kind == 1 || op[i].kind == 3)
			{
				int k = op[i].v;
				int res = query(1, cnt, op[i].l, op[i].r, 0, k);
				if (op[i].kind == 1)
					s1 += res;
				else
					s3 += res;
			}
			else
			{
				int li = 1;
				int ri = op[i].r - op[i].l + 1;
				while (li < ri)
				{
					int k = li + (ri - li + 1) / 2;
					int tmp = query(1, cnt, op[i].l, op[i].r, 0, k);
					if (tmp <= op[i].v)
						li = k;
					else
						ri = k - 1;
				}
				s2 += li;
			}
		}
		cout << s1 << endl;
		cout << s2 << endl;
		cout << s3 << endl;
	}
	return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值