一.题目链接:
SPOJ-DQUERY
二.题目大意:
求区间 [l, r] 中不同元素的个数.
三.分析:
先考虑区间右端点 r 的情形.
设有 5 个元素{1,2,2,3,5},每个元素最后出现的位置为{1,0,1,1,1}.
那么,区间[1,5]中不同元素的个数为 sum[5] - sum[0].
区间[1,4]中不同元素的个数为 sum[5] - sum[1].
其他区间同理.
现在再来考虑区间右端点变化的情形.
我们只需对每个右端点建立一颗线段树(区间求和)即可.
当然了,这里肯定要建主席树的.
详见代码.
四.代码实现:
#include <set>
#include <map>
#include <ctime>
#include <queue>
#include <cmath>
#include <stack>
#include <bitset>
#include <vector>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define eps 1e-8
#define lc k * 2
#define rc k * 2 + 1
#define pi acos(-1.0)
#define ll long long int
using namespace std;
const int M = (int)1e6;
const ll mod = (ll)1e9 + 7;
const int inf = 0x3f3f3f3f;
struct node
{
int sum;
int l, r;
} tree[M * 40 + 5];
int cnt;
int root[M + 5];
int a[M + 5];
map <int, int> pos;
void update(int l, int r, int &x, int y, int pos, int v)
{
tree[++cnt] = tree[y];
tree[cnt].sum += v;
x = cnt;
if(l == r)
return;
int mid = (l + r) >> 1;
if(pos <= mid)
update(l, mid, tree[x].l, tree[y].l, pos, v);
else
update(mid + 1, r, tree[x].r, tree[y].r, pos, v);
}
int query(int l, int r, int x, int pos)
{
if(l >= pos)
return tree[x].sum;
int mid = (l + r) >> 1;
int sum = 0;
if(pos <= mid)
sum += query(l, mid, tree[x].l, pos);
sum += query(mid + 1, r, tree[x].r, pos);
return sum;
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
if(!pos.count(a[i]))///不存在的话直接加入即可.
{
update(1, n, root[i], root[i - 1], i, 1);
}
else///如果之间存在,则需将之前的位置 - 1,在建立一条新链. 不过这里之间建立了两条链,一条 - 1,另一条 + 1.
{
int tmp;///记录 - 1 链的树根编号.
update(1, n, tmp, root[i - 1], pos[a[i]], -1);
update(1, n, root[i], tmp, i, 1);
}
pos[a[i]] = i;
}
int m;
scanf("%d", &m);
int l, r;
while((m--) > 0)
{
scanf("%d %d", &l, &r);
printf("%d\n", query(1, n, root[r], l));///查询区间和
}
return 0;
}