您的位置:首页 > 产品设计 > UI/UE

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

2014-04-16 10:36 369 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  SPOJ3267