牛客网多校练习赛 1 JDifferent Integers (签到题) 树状数组+技巧

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

题目描述

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.
#include<iostream>
#include<algorithm>
#include<string>
#include<map>//int dx[4]={0,0,-1,1};int dy[4]={-1,1,0,0};
#include<set>//int gcd(int a,int b){return b?gcd(b,a%b):a;}
#include<queue>
#include<cmath>
#include<stack>
#include<string.h>
#include<stdlib.h>
#include<cstdio>
#define mod 1e9+7
#define ll long long
#define maxn 200005
#define MAX 500005
#define ms memset
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000") ///在c++中是防止暴栈用的
int n,m;
int seq[maxn];
int show[maxn],Next[maxn];
bool fir[maxn];
/*
题目大意:给定n个数,和m个查询,
只不过查询的不是完整的区间,而是
两端构成的两个区间,查询总共多少个不同的数字。

首先一个投机取巧的方式是:把其当成一个区间处理,
类似与字符串处理的技巧,倍增一个即可。
对于一个区间如何维护呢?
首先引入:fir数组表示a[i]在i位置是否是第一次出现的位置,
那么fir是一个01数组,其不同的数的个数就可以通过区间里
对1的计数构成。
而next数组保存着i数值的下一个出现位置,为了及时更新fir数组而存在。
而show数组维护者递推关系,当没到达左边界时要更新fir数组直到到达未知。

这时就用树状数组维护一下,即对没到达边界的数全都-1.

*/
struct node
{
    int l,r,id;
    node(){}
};
node q[maxn/2];
bool cmp(node x,node y)
{
    return x.l<y.l;
}

int sum[maxn],res[maxn];///res数组存储结果,sum存储前缀答案
int lowbit(int x){ return x&(-x);}
void modify(int p,int v){    for(;p<=n;sum[p]+=v,p+=lowbit(p));}
int query(int p)
{
    int ret=0;
    for(;p>0;ret+=sum[p],p-=lowbit(p));
    return ret;
}

void init()
{
    memset(sum,0,sizeof(sum));
    memset(res,0,sizeof(res));
    memset(show,0,sizeof(show));
    memset(fir,0,sizeof(fir));
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
    init();
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&seq[i]);
        seq[i+n]=seq[i];
    }
    n*=2;

    for(int i=n;i>=1;i--)///从后往前递推来构造映射
    {
        if(!show[seq[i]])
        {
            show[seq[i]]=i;
            fir[i]=true;
        }
        else
        {
            Next[i]=show[seq[i]];///下一个这样的数出现的位置
            fir[Next[i]]=false;
            fir[i]=true;///回退
            show[seq[i]]=i;
        }
    }

    int l,r;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&l,&r);
        l+=(n/2);  swap(l,r);
        q[i].l=l,q[i].r=r;
        q[i].id=i;
    }
    
    sort(q+1,q+m+1,cmp);

    for(int i=1;i<=n;i++)
        if(fir[i]) modify(i,1);///更新前缀和。前缀和是不同数字的个数

    int ptr=1;
    for(int i=1;i<=m;i++)
    {
        for(;ptr<q[i].l;ptr++)///把左边界维护成递增的序列
        {
            if(fir[ptr])
            {
                fir[ptr]=false;
                modify(ptr,-1);
                if(Next[ptr])
                {
                    fir[Next[ptr]] = true;
                    modify( Next[ptr],1 );
                }
            }
        }
        res[q[i].id]=query(q[i].r)-query(q[i].l-1);///每次查询的复杂度是logn
    }

    for(int i=1;i<=m;i++) printf("%d\n",res[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37451344/article/details/81149718
今日推荐