牛客网暑假acm多校训练营(第一场)J题解

 

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个数,查询0-l到r-n的区间内不同数的个数。

 

 运用到树状数组存储。减少运行时间。

步骤:

1、初始化所有变量,fir初始化为1,其余为0。

2、加倍数组,使得两个区间可以变为一个区间进行计算。

3、从后往前,统计所有数字的数目,同时记录出现两次以上数目最早所在的位置。如只出现过一次的数,fir为1,next为0。将fir=1的位置加入树状数组。

4、输入区间,使其变为一个区间,并对区间进行排序。

5、从左开始遍历,当遇到区间时,计算两个区间树状数组的值。没遇到区间的时候不断地将fir的值往后移,移到next的位置。

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

using namespace std;

const int maxnn=100010;
const int maxn=2*maxnn;
struct ql
{
	int l,r,pos;
}lr[maxn];

int loc[maxnn],ans[maxnn];
int a[maxn],c[maxn],fir[maxn],Next[maxn];//fir用来 记录出现一次的个数,next用来记录上一次出现的位置 

bool cmp(ql p, ql q) {//           排序 
    return p.l <q.l ;
}

int lowbit(int x) {//   树状数组处理 
    return x & -x;
}

void add(int x, int v) { // 单点更新 
    while (x < maxn) {
        c[x] += v;
        x += lowbit(x);
    }
}

int sum(int x) { //      树状数组处理 
    int s = 0;
    while (x) {
        s += c[x];
        x -= lowbit(x);
    }
    return s;
}



int main()
{
	int n,q;
	while(scanf("%d%d",&n,&q)!=EOF)
	{
		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));//初始化变量,但fir数组需要初始化为1.
		
		 for(int i=1;i<=n;i++)
		 {
		 	scanf("%d",&a[i]);
		 	a[i+n]=a[i];//加倍数组,使两个区间变为一个区间
			fir[i]=1;
			fir[i+n]=1;//fir数组初始化为0 
		 }
		 
		 for(int i=2*n;i>0;i--)
		 {
		 	fir[loc[a[i]]]=0;//当出现两个相同的数字时 fir归0 
			Next[i]=loc[a[i]];//当出现两个以上相同数字时,记录后一个出现的位置 
			loc[a[i]]=i;
		 }
		 
		 for (int i = 1; i <= 2 * n; i++)
		  {
            if (fir[i]) 
			{
                add(i, 1);//当fir=1时,加入树状数组 
            }
          }
           for (int i = 1; i <= q; i++) 
		{
            scanf("%d %d", &lr[i].l, &lr[i].r);
            lr[i].l += n;
            int temp;
			temp=lr[i].l;
			lr[i].l =lr[i].r;
			lr[i].r=temp;//取一个区间 
            lr[i].pos = i;//记录点 
        }
        sort(lr+1,lr+q+1,cmp);//数组排序 
          
          
          int k = 1;//从左开始往右到x 
        for (int i = 1; i <= 2 * n && k <= q;) 
		{
            if (i == lr[k].l) 
			{
                ans[lr[k].pos] = sum(lr[k].r) - sum(lr[k].l) + 1;//树状数组从r到l 
                k++;
            } 
			else 
			{
                if (fir[i]) //在1-x区间内去除fir的值,同时将fir的值归到下一个相同值的位置 
				{
                    fir[i] = 0; 
                    add(i, -1);//树状数组去除1 
                    fir[Next[i]] = 1;
                    add(Next[i], 1);//树状数组加1 
                }
                i++;
            }

        }
        
        for (int i = 1; i <= q; i++) 
		{
            printf("%d\n", ans[i]);//输出从l到r的树状数组之和 
        }
	}
} 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值