2013 Asia Regional Contest Problem H --- Number Squence (树状数组 + 离线)

链接:  http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1792

Description

Ikki最近又喜欢上了数列游戏,她现在想出个问题考考你,问题是这样的:

首先给你一个由n个整数组成的序列然后Ikki会给出一系列的提问:知道了区间的两个端点s和e,那么这个区间内有多少个不同的数字?

Input

多组测试数据,处理到文件结束,对于每组数据:

第一行输入两个整数n,q分别表示序列中数的个数和Ikki提问的次数

(0<n≤100000,0<q≤100000)。

第二行输入n个整数xi~xn表示序列中每个数的大小。(0 <= x <= 109 )

接下来的q行每行输入两个正整数a,b(a≤b)表示询问的区间为[a,b]。(序列下标从1开始)

Output

对于每组测试数据,输出q行依次表示每个提问的答案。

Sample Input

6 3
1 2 1 2 3 4
1 2
1 3
2 5

Sample Output
2
2
3


分析:

我们可以将查询区间按右端点排序,从左向右枚举i,维护一个树状数组,其中getsum(k)表示从k到i不同的数字有多少个,考虑到将第i个数字加入,这时候,树状数组里面发生改变的仅仅是
last[v] +1 到 i 这个区间的getsum()值,其中v是第i位数字,last[v]表示v这个数上次出线的位置,如果没有出现就为0。所以,我们把树状数组last[v]+1这个位置的值+1,把i+1这个位置的值-1,这样求和的时候,只要是左端点在last[v]+1之后的(包括自己),就能将v这点给包括进来了。然后枚举到i之后,我们考虑右端点是i的全部查询,对其左端点在树状数组中求和就可以了。



代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
#define MAXN 100005
#define RST(N)memset(N, 0, sizeof(N))
using namespace std;

struct Node
{
    int l, r;
    int x;
}q[MAXN];

int n, Q, pre[1000001], val[MAXN], tre[MAXN];
int tmp[MAXN], xu[MAXN], p[MAXN], res[MAXN], a, b;

bool cmp(Node a, Node b) { return a.r < b.r; }

inline int lowbit(int x) { return x & (-x); }

void add(int pos, int x)
{
    while(pos <= n) {
        tre[pos] += x;
        pos += lowbit(pos);
    }
}

int getsum(int pos)
{
    int sum = 0;
    while (pos > 0) {
        sum += tre[pos];
        pos -= lowbit(pos);
    }
    return sum;
}

int bin_search(int x)
{
    int low = 1, high = n, mid;
    while(low <= high) {
        mid = (low + high) >> 1;
        if(tmp[mid] > x) high = mid - 1;
        else if(tmp[mid] < x) low = mid + 1;
        else return xu[mid];
    }
}

void Init()
{
    sort(tmp+1, tmp+n+1);
    int m = 1;
    xu[1] = 1;
    for(int i=2; i<=n; i++) {
        if(xu[i] != xu[i-1]) xu[i] = ++m;
        else xu[i] = m;
    }
    for(int i=1; i<=n; i++) val[i] = bin_search(val[i]);
    RST(tre), RST(p);
}

void solve(int Q)
{
    int m = 0;
    for(int i=1; i<=n; i++) {
        pre[i] = p[val[i]];
        p[val[i]] = i;
    }
    for(int i=1; i<=n; i++) {
        add(pre[i]+1, 1);
        add(i+1, -1);
        while(m < Q && q[m].r == i) {
            res[q[m].x] = getsum(q[m].l);
            m++;
        }
    }
    for(int i=0; i<Q; i++) printf("%d\n",res[i]);
}

int main()
{
    while(~scanf("%d %d", &n, &Q)) {
        for(int i=1; i<=n; i++) {
            scanf("%d", &val[i]);
            tmp[i] = val[i];
            xu[i] = i;
        }
        Init();
        for(int i=0; i<Q; i++) {
            scanf("%d %d", &q[i].l, &q[i].r);
            q[i].x = i;
        }
        sort(q, q+Q, cmp);
        solve(Q);
    }
    return 0;
}




你可能感兴趣的:(树状数组,离线,2013亚洲区赛题)