7.19牛客多校第一场 J-Different Integers 树状数组

10. 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. 
备注
1 ≤ n, q ≤ 1e5
1 ≤ ai ≤ n
1 ≤ li, ri ≤ n
The number of test cases does not exceed 10. 
示例1: 
输入 
3 2 
1 2 1 
1 2 
1 3 
4 1 
1 2 3 4 
1 3 
输出 
2 1 3

题目大意给n个数,q次查询,接下来q行表示q个操作,l和r,操作为从1到l,r到n区间内找有几个不同的数

想法题意就读错,是输入l,r,在1到l,r到n找不同的数,我想成了从l 到n区间内,对于此题没什么想法,数据1e5,暴力二重循环要超时,其他数据结构也不知道,后来通过学长的讲解,知道可以用树状数组,复杂度nlogn,现在可以套板子,原理还没搞懂,毕竟看学长的代码就很难看懂了。。。自己写更不可能了,以下代码来自学长。。。

思路

首先考虑将两个区间并成一个区间,将原数组扩展为2倍长的,即 a[i+n] = a[i],首尾相连。对于查询a[1...l]和a[r..n]有多少种不同的数字可以转换为查询 a[r...l+n]有多少种不同的数字。

然后开两个数组,firs和next,first[i]记为1表示这个位置的数第一次出现,以下再出现记为0,之后树状数组查询更新用的first数组。next数组记录这一个数下一次出现的位置。

离线查询,对依次输入的区间按左端点进行排序,用sort(属于c++的stl内容,这两天刚搞懂,需要再系统了解下stl,毕竟很方便,常常因为不会c++而与你们格格不入),这样区间就是一直右移的,就可以只刷新要用的右半部分树状数组。

最难的是数组更新与维护,下标从0开始遍历,如果等于输入的需要查询的左端点,就可以统计1的个数了,用树状数组查询得结果sum(y)-sum(x-1)。否则fir[Next[i]]=1,更新下一个为1。

树状数组模板

更新,第x个数更新为y。

void update(int x,int y)
{
    while(x<=n)
    {
        c[x]+=y;
        x+=lowbit(x);
    }
}

树状数组维护的是数组的前缀和,比如sum(x)就是a[x]的前缀和,想查询l~r区间的元素和只需要求出来sum(r)-sum(l-1),这里的sum函数十分的神奇:

int sum(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}

其中学长的三行代码,,我理解了一天,,(灬ꈍ ꈍ灬),

数组循环完是这样子的。。。

a[i] 1 2 1 1 2 1
first[i] 1 1 0 0 0 0
next[i] 3 5 4 6 0 0
        //预处理Next、Fir数组
        for (int i = 2 * n; i > 0; i--) {
            fir[loc[a[i]]] = 0;
            Next[i] = loc[a[i]];
            loc[a[i]] = i;
        }
//Name:牛客多校第一场 J-Different Integers 树状数组
//============================================================================
#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;
}

猜你喜欢

转载自blog.csdn.net/qq_42079027/article/details/81139803