Different Integers

题目:

Given a sequence of integers a1, a2, ..., an and q pairs of integers (l 1, r1), (l2, r2), ..., (lq, rq), find count(l1, r1), count(l2, r2), ..., count(lq, rq) where count(i, j) is the number of different integers among a 1, a2, ..., ai, aj, aj + 1,

..., an.

输入描述:

The input consists of several test cases and is terminated by end-of-file.

The first line of each test cases contains two integers n and q.

The second line contains n integers a 1, a2, ..., an.

The i-th of the following q lines contains two integers l i and ri.

输出描述:

For each test case, print q integers which denote the result.

备注

 3 2

1 2 1

1 2

1 3

4 1

1 2 3 4

1 3

输出

2

1

3


 

题目大意:输入一组数据(有n个数)和多组区间,要求每次查询求a[1...l]和a[r...n]有多少种不同的数字,即查询两个区间内不同数的个数。

思路:记第一个数字i出现的下标为first[i],最后一个数字出现的下标为last[i],设a[1]到a[n]中不同数字个数为tot,则对于每一个查询(l,n),可以把问题转化为求有多少个i满足l < first[i]且last[i] < r , 记这个数为cnt,则答案为tot-cnt.                                  也可以将(l[i], r[i])看成平面上一点,则问题转化为二维平面上某点右下方有多少个点。扫描即可。
时间复杂度O(nlogn)

其中需要用树状数组维护

树状数组的原理和实现:

                  https://blog.csdn.net/flushhip/article/details/79165701

                  https://www.cnblogs.com/George1994/p/7710886.html

也可以用可持久化线段树来实现

原理:https://www.cnblogs.com/TenosDoIt/p/3453089.html

           https://www.cnblogs.com/RabbitHu/p/segtree.html

完整代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;
const int maxn = 100005;
const int MAXN = 2 * maxn;
struct ss {
    int x , y, pos;
} f[maxn];
//loc[i] = i出现的位置
int loc[maxn], ans[maxn];
//Next[i] = i位置上的数下一个出现的位置
//fir[i] = i位置上的数是否为第一次出现
int a[MAXN], c[MAXN], Next[MAXN], fir[MAXN];

bool cmp(ss p, ss q) {
    return p.x < q.x;
}

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

//a[i] += v
void add(int x, int v) {
    while (x < MAXN) {
        c[x] += v;
        x += lowbit(x);
    }
}

//a[1] + ... + a[x]
int sum(int x) {
    int s = 0;
    while (x) {
        s += c[x];
        x -= lowbit(x);
    }
    return s;
}

//void display(int n) {
//	for(int i = 1; i <= 2 * n; i++) {
//		printf("i = %d a[i] = %d next[i] = %d fir[i] = %d sum = %d\n", i, a[i], next[i], fir[i], sum(i));
//	}
//}

int main() {
    int n, q;
    while (~scanf("%d %d", &n, &q)) {

        //初始化变量,Fir初始化为1, 其余初始化为0
        memset(a, 0, sizeof(a));
        memset(loc, 0, sizeof(loc));
        memset(Next, 0, sizeof(Next));
        memset(ans, 0, sizeof(ans));
        memset(c, 0, sizeof(c));

//		倍增数组,将对两个区间的查询转换为对单个区间的查询
        for (int i = 1; i <= n; i++) {
//			cin>>a[i];
            scanf("%d", &a[i]);
            a[i + n] = a[i];
            fir[i] = 1;
            fir[i + n] = 1;
        }

        //预处理Next、Fir数组
        for (int i = 2 * n; i > 0; i--) {
            fir[loc[a[i]]] = 0;
            Next[i] = loc[a[i]];
            loc[a[i]] = i;
        }

        //预处理树状数组
        for (int i = 1; i <= 2 * n; i++) {
            if (fir[i]) {
                add(i, 1);
            }
        }

//		for(int i = 1; i <= 2 * n; i++) {
//			printf("i = %d a[i] = %d next[i] = %d fir[i] = %d sum = %d\n", i, a[i], next[i], fir[i], sum(i));
//		}


        //区间处理,
        for (int i = 1; i <= q; i++) {
            scanf("%d %d", &f[i].x, &f[i].y);
//			cin>>f[i].x>>f[i].y;
            f[i].x += n;
            swap(f[i].x, f[i].y);
            f[i].pos = i;
        }

//		区间排序
        sort(f + 1, f + q + 1, cmp);

//		for (int i =1; i <= q; i++) {
//			printf("f[i].x = %d f[i].y = %d f[i].pos = %d\n", f[i].x, f[i].y, f[i].pos);
//		}

        int nextLIndex = 1;
        for (int i = 1; i <= 2 * n && nextLIndex <= q;) {
            if (i == f[nextLIndex].x) {
// Query
                ans[f[nextLIndex].pos] = sum(f[nextLIndex].y) - sum(f[nextLIndex].x) + 1;
                nextLIndex++;
//				printf("f[i].pos = %d ans = %d\n", f[i].pos, ans[f[i].pos]);
            } else {
                if (fir[i]) {
// Update,下一个更新为 1
                    fir[i] = 0;
                    add(i, -1);
                    fir[Next[i]] = 1;
                    add(Next[i], 1);
//					display(n);
                }
                i++;
            }

        }

        for (int i = 1; i <= q; i++) {
//			cout<<ans[i]<<endl;
            printf("%d\n", ans[i]);
        }

    }

    return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值