SPOJ3267--D-query(离线线段树||在线主席树)

Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence ai, ai+1, ..., aj.

Input

  • Line 1: n (1 ≤ n ≤ 30000).
  • Line 2: n numbers a1, a2, ..., an (1 ≤ ai ≤ 106).
  • Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
  • In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).

Output

  • For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1, ..., aj in a single line.

Example

Input
5
1 1 2 1 3
3
1 5
2 4
3 5

Output
3
2
3 
题意:求区间内元素的种数
思路:如果有一个询问u,v。则ans[v].push_back(u)。
      然后就从左往右扫描,先更新数,如果没出现过很好处理。如果出现过,删除之前的出现记录,然后出现点更新为这个地方。然后如果这个点有      询问,就query一下。
      线段树维护区间内的数种数。
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 30800
#define maxm 1008000
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int pos[maxm],key[maxn],Ans[208000];
struct ST
{
	int l,r,sum;
}st[maxn<<2];
void PushUp(int id)
{
	st[id].sum = st[id<<1].sum + st[id<<1|1].sum;
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r;
	st[id].sum = 0;
	if(l == r)	return;
	int mid = (l+r) >> 1;
	buildtree(lson);
	buildtree(rson);
}
void Update(int id,int p)
{
	if(st[id].l == p && st[id].r == p)
	{
		st[id].sum ^= 1;
		return;
	}
	if(st[id<<1].r >= p)
		Update(id<<1,p);
	else Update(id<<1|1,p);
	PushUp(id);
}
int Query(int id,int l,int r)
{
	if(st[id].l == l && st[id].r == r)
		return st[id].sum;
	if(st[id<<1].r >= r)
		return Query(id<<1,l,r);
	else if(st[id<<1|1].l <= l)
		return Query(id<<1|1,l,r);
	return Query(id<<1,l,st[id<<1].r) + Query(id<<1|1,st[id<<1|1].l,r);
}
vector <int> ans[maxn];
vector <int> num[maxn];
int main()
{
	//freopen("in.txt","r",stdin);
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		memset(pos,0,sizeof(pos));
		for(int i = 1;i <= n;i++)	scanf("%d",&key[i]);
		for(int i = 1;i <= n;i++)	
		{
			num[i].clear();
			ans[i].clear();
		}
		int m;	scanf("%d",&m);
		for(int i = 1;i <= m;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			ans[v].push_back(u);
			num[v].push_back(i);
		}
		buildtree(1,1,n);
		for(int i = 1;i <= n;i++)
		{
			if(pos[key[i]])
				Update(1,pos[key[i]]);
			Update(1,i);
			pos[key[i]] = i;
			for(int j = 0;j < ans[i].size();j++)
			{
				int u = ans[i][j];
				Ans[num[i][j]] = Query(1,u,i);
			}
		}
		for(int i = 1;i <= m;i++)	printf("%d\n",Ans[i]);
	}
	return 0;
}

此题也可在线做,用主席树。
给数组的每一个后缀都建立一棵线段树,然后从后往前。类似于上面线段树的做法,线段树中一个数之前出现过,从左往右扫的过程中不断将这个数出现的位置往右更新。。而主席树我从右往左扫,不断将数出现的位置往左更新。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 30080
#define maxm 6008000
int T[maxn],key[maxn];
int Pos[1008000];
int c[maxm],lson[maxm],rson[maxm];
int tot,n;
int build(int l,int r)
{
	int root = tot++;
	c[root] = 0;
	if(l != r)
	{
		int mid = (l+r) >> 1;
		lson[root] = build(l,mid);
		rson[root] = build(mid+1,r);
	}
	return root;
}

int update(int root,int pos,int val)
{
	int newnode = tot++,tmp = newnode;
	c[newnode] = c[root] + val;
	int l = 1,r = n;
	while(l < r)
	{
		int mid = (l+r) >> 1;
		if(pos <= mid)
		{
			lson[newnode] = tot++;	rson[newnode] = rson[root];
			newnode = lson[newnode];	root = lson[root];
			r = mid;
		}
		else 
		{
			rson[newnode] = tot++;	lson[newnode] = lson[root];
			newnode = rson[newnode];	root = rson[root];
			l = mid + 1;
		}
		c[newnode] = c[root] + val;
	}
	return tmp;
}

int query(int root,int pos)
{
	int ret = 0;
	int l = 1,r = n;
	while(pos < r)
	{
		int mid = (l+r) >> 1;
		if(pos <= mid)
		{
			r = mid;
			root = lson[root];
		}
		else 
		{
			ret += c[lson[root]];
			root = rson[root];
			l = mid + 1;
		}
	}
	return ret + c[root];
}

int main()
{
	//freopen("in.txt","r",stdin);
	while(scanf("%d",&n)!=EOF)
	{
		memset(Pos,0,sizeof(Pos));
		tot = 0;
		for(int i = 1;i <= n;i++)
			scanf("%d",&key[i]);
		T[n+1] = build(1,n);
		for(int i = n;i;i--)
		{
			if(Pos[key[i]])//如果这个数曾经出现过
			{
				int t = update(T[i+1],Pos[key[i]],-1);
				Pos[key[i]] = i;
				T[i] = update(t,i,1);
			}
			else 
			{
				Pos[key[i]] = i;
				T[i] = update(T[i+1],i,1);
			}
		}
		int m;	scanf("%d",&m);
		while(m--)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			printf("%d\n",query(T[l],r));
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值