2018牛客多校训练1-J(树状数组)

链接:https://www.nowcoder.com/acm/contest/139/J
来源:牛客网
 

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld

题目描述

Given a sequence of integers a1, a2, ..., an and q pairs of integers (l1, 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 a1, 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 a1, a2, ..., an.
The i-th of the following q lines contains two integers li and ri.

输出描述:

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

 

示例1

输入

复制

3 2
1 2 1
1 2
1 3
4 1
1 2 3 4
1 3

输出

复制

2
1
3

备注:

* 1 ≤ n, q ≤ 105
* 1 ≤ ai ≤ n
* 1 ≤ li, ri ≤ n
* The number of test cases does not exceed 10.

题意:给出一串数字以及q次查询,每次查询给出[l,r],要求求出[1,l]+[r,n]的所有不相同的数字个数。

思路:第一种思路是先对数组进行倍增,变为两倍长,然后查询就变成一个完整的区间,离线处理,按r从小到大排序,数组从1到2n扫一遍,每次更新每种数组最后出现的位置,用树状数组处理,把前一次出现位置在树状数组里面更新-1,这次位置更新+1,然后如果扫描到的i==r则对查询区间进行sum查询。

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 100005;
struct node
{
    int l,r;
    int id;
}stu[maxn];
bool cmp(node a,node b)
{
    return a.r<b.r;
}
int n,q;
int c[maxn*2], a[maxn*2];   
int last[maxn];
int ans[maxn];
int Lowbit(int x)  // 2^k  Lowbit(x)为x保留最右边1的结果
{  
    return x&(-x);  
}  
void update(int i, int x)//i点增量为x ,更新树状数组 
{  
    while(i <= n*2)  
    {  
        c[i] += x;  
        i += Lowbit(i);  
    }  
}  
int sum(int x)//区间求和 [1,x]  
{  
    int sum=0;  
    while(x>0)  
    {  
        sum+=c[x];  
        x-=Lowbit(x);  
    }  
    return sum;  
}  
int Getsum(int x1,int x2) //求任意区间和  
{   
    return sum(x2) - sum(x1-1);   
} 
int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        memset(last,0,sizeof(last));
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            a[i+n]=a[i];
        }
        int x,y;
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d",&x,&y);
            stu[i].l=y;
            stu[i].r=n+x;
            stu[i].id=i;
        }
        sort(stu+1,stu+1+q,cmp);
        int k=1;
        for(int i=1;i<=2*n&&k<=q;i++)
        {
            if(last[a[i]])
                update(last[a[i]],-1);
            last[a[i]]=i;
            update(last[a[i]],1);
            while(k<=q&&stu[k].r<=i)
            {
                ans[stu[k].id]=Getsum(stu[k].l,stu[k].r);
                k++;
            }
        }
        for(int i=1;i<=q;i++)
        {
            printf("%d\n",ans[i]);
        }
    }
}

第二种思路:差不多,需要记录下每个数字第一次出现位置,及最后一次出现位置,不需要倍增,查询按r从小到大排序,数组1到n扫一遍,树状数组记录到目前的r为止,每个对应的l,中间段的与两边不同的数字个数,如果i==r,进行查询,总数减去查询的sum,如果这个i是a[i]最后出现的位置,那么找到对应的第一次出现位置first,first之前的l其中间段的数字数需要++。

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 100005;
struct node
{
    int l,r;
    int id;
}stu[maxn];
bool cmp(node a,node b)
{
    return a.r<b.r;
}
int n,q;
int c[maxn*2], a[maxn*2];   
int last[maxn];
int ans[maxn];
int Lowbit(int x)  // 2^k  Lowbit(x)为x保留最右边1的结果
{  
    return x&(-x);  
}  
void update(int i, int x)//i点增量为x ,更新树状数组 
{  
    while(i <= n*2)  
    {  
        c[i] += x;  
        i += Lowbit(i);  
    }  
}  
int sum(int x)//区间求和 [1,x]  
{  
    int sum=0;  
    while(x>0)  
    {  
        sum+=c[x];  
        x-=Lowbit(x);  
    }  
    return sum;  
}  
int Getsum(int x1,int x2) //求任意区间和  
{   
    return sum(x2) - sum(x1-1);   
} 
int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        memset(last,0,sizeof(last));
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            a[i+n]=a[i];
        }
        int x,y;
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d",&x,&y);
            stu[i].l=y;
            stu[i].r=n+x;
            stu[i].id=i;
        }
        sort(stu+1,stu+1+q,cmp);
        int k=1;
        for(int i=1;i<=2*n&&k<=q;i++)
        {
            if(last[a[i]])
                update(last[a[i]],-1);
            last[a[i]]=i;
            update(last[a[i]],1);
            while(k<=q&&stu[k].r<=i)
            {
                ans[stu[k].id]=Getsum(stu[k].l,stu[k].r);
                k++;
            }
        }
        for(int i=1;i<=q;i++)
        {
            printf("%d\n",ans[i]);
        }
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值