2016CCPC长春:Sequence II(主席树)

问题概述:n个数q次查询,每次查询(l,r)表示查询区间[l,r]内所有不同数字第一次出现的下标,输出这些下标中位

特殊:如果下标数sum为偶数个,则输出第sum/2个下标,且强制在线,每一组的l和r都跟上一组的答案有关

(HDU 5919:http://acm.hdu.edu.cn/showproblem.php?pid=5919

输入样例:                            对应输出:

1                                            Case #1: 3 1

5 2

2 5 2 1 2

2 3

2 4


解题思路:

将数组倒过来建树,每当添加一个数字时,设这个数字的下标为i,这个数字上次出现的下标为j,则下标i对应

的val值+1,下标j对应的val值-1,每次查询区间[l,r]直接询问版本为l的树即可,先找出在这个区间内的val值之和

sum,然后在这个区间内找到第(sum+1)/2个值为1的下标就是答案


#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef struct
{
	int l, r;
	int val;
}Ctree;
Ctree s[8000005];
int n, tot, a[200005], t[200005], flag[200005], prt[200005];
int Build(int l, int r);
int Update(int root, int x, int k);
int Query(int root, int l, int r, int a, int b);
int Find(int root, int k);
int main(void)
{
	int T, i, q, l, r, ans, sum, k, temp, cas = 1;
	scanf("%d", &T);
	while(T--)
	{
		ans = tot = 0;
		memset(flag, -1, sizeof(flag));
		scanf("%d%d", &n, &q);
		for(i=1;i<=n;i++)
			scanf("%d", &a[i]);
		t[n+1] = Build(1, n);
		for(i=n;i>=1;i--)
		{
			t[i] = Update(t[i+1], i, 1);
			if(flag[a[i]]!=-1)
				t[i] = Update(t[i], flag[a[i]], -1);		/*直接另起一棵树,虽然有点浪费空间*/
			flag[a[i]] = i;
		}
		k = 0;
		while(q--)
		{
			scanf("%d%d", &l, &r);
			temp = l;
			l = min((l+ans)%n+1, (r+ans)%n+1);
			r = max((temp+ans)%n+1, (r+ans)%n+1);
			sum = Query(t[l], 1, n, l, r);
			ans = Find(t[l], (sum+1)/2);
			prt[++k] = ans;
		}
		printf("Case #%d:", cas++);
		for(i=1;i<=k;i++)
			printf(" %d", prt[i]);
		printf("\n");
	}
	return 0;
}

int Build(int l, int r)		/*建空树,所有节点val值为0*/
{
	int m, root;
	m = (l+r)/2;
	root = ++tot;
	if(l==r)
	{
		s[root].val = 0;
		return root;
	}
	s[root].l = Build(l, m);
	s[root].r = Build(m+1, r);
	s[root].val = s[s[root].l].val+s[s[root].r].val;
	return root;
}

int Update(int root, int x, int k)		/*修改第x个下标的值*/
{
	int now, temp, m, l, r;
	temp = now = ++tot;
	l = 1, r = n;
	while(l<r)
	{
		m = (l+r)/2;
		s[now].val = s[root].val+k;
		if(x<=m)
		{
			s[now].l = ++tot;
			s[now].r = s[root].r;
			root = s[root].l;
			r = m;
			now = tot;
		}
		else
		{
			s[now].l = s[root].l;
			s[now].r = ++tot;
			root = s[root].r;
			l = m+1;
			now = tot;
		}
	}
	s[now].val = s[root].val+k;
	return temp;
}

int Query(int root, int l, int r, int a, int b)
{
	int m, sum;
	if(l>=a && r<=b)
		return s[root].val;
	m = (l+r)/2;
	sum = 0;
	if(a<=m)
		sum += Query(s[root].l, l, m, a, b);
	if(b>=m+1)
		sum += Query(s[root].r, m+1, r, a, b);
	return sum;
}

int Find(int root, int k)
{
	int l, r, m;
	l = 1, r = n;
	while(l<r)
	{
		m = (l+r)/2;
		if(s[s[root].l].val>=k)
		{
			root = s[root].l;
			r = m;
		}
		else
		{
			k -= s[s[root].l].val;
			root = s[root].r;
			l = m+1;
		}
	}
	return l;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值