一.题目链接:

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;
}