树状数组+离线查询预处理 7.19牛客暑期多校训练营一 J . Different Integers

J .   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.备注

  • 1 ≤ n, q ≤ 1e5

  • 1 ≤ ai ≤ n

  • 1 ≤ li, ri ≤ n

  • The number of test cases does not exceed 10.示例1:输入3 21 2 11 21 34 11 2 3 41 3输出2 1 3

题目大意:

给一个1到N个数,Q次查询。每次查询L,R,返回1到L,R到N两个区间内不同的数字的数量。

想法:

1e5次查询,这个for循环要有的,用set暴力找不同的话,又来1e5个数循环,n^2复杂度铁定超时。可以知道一次查询至多logn。区间查询啊,容易想到树状数组。不过不知道树状数组的节点该存什么(一个节点一个set好了,笑)。这是第一个有很多人做出来的,但感觉不是签到题,卡了很久然后去做别的了。感谢曦哥挽回了爆零的局面。以下解题思路由曦哥提供

思路:

1、查询区间内不同的数,就是区间内有哪些数是第一次出现的(相对于这些数后面)。它们的数量即查询结果。

2、树状数组查找区间不同数的板子。 节点存原数组区间内有几个数字是第一次出现的。

3、把两个查询区间合并成一个,简化问题。就是把数组加倍,首尾相连。那么一次LR查询区间就是,R到N,N到N+L的一个区间。

4、离线查询。将查询按l排序,这样区间就是一直右移的,就可以只刷新要用的右半部分树状数组。不然每次从头刷新(而且这个做不到的吧)。

过程:

离线存查询q[],合并区间,l=R,r=N+L。q[]按r降序排。从q[]中最大r开始刷一遍next[]。(next[i]:i位置上数字在后面再次出现的位置。用于更新first数组)也刷一遍first[] 。(first[i]:i位置上的数字是不是在区间内第一次出现。是树状数组的原数组)。根据first[]初始化树状数组(区间节点存该区间内first为true的个数,即查询结果)。

遍历q[],l、r传入树状数组得结果。在每次次遍历前,因为l右移,l前的first[]要更新到l后,fir[next[i]]=true,同时更新树状数组。

AC代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
 
 
using namespace std;
 
 
int N,M;
const int SIZE = 250005;
int c[SIZE];
int A[SIZE];
int Next[SIZE];
int res[200005];
int show[2000005];
bool fir[SIZE];
 
 
struct Q
{
    int l,r;
    int pos;
}q[200005];
 
 
int lowbit(int k)
{
    return k&(-k);
}
 
 
void modify(int n,int v)
{
    while(n <= N)
    {
        c[n] += v;
        n += lowbit(n);
    }
}
 
 
int sum(int n)
{
    int ans = 0;
    while(n > 0)
    {
        ans += c[n];
        n -= lowbit(n);
    }
    return ans;
}
 
 
int cmp(Q a, Q b)
{
    return a.l < b.l;
}
 
 
int main()
{
    while(~scanf("%d",&N)){
        memset(res,0,sizeof(res));
        memset(c,0,sizeof(c));
        memset(A,0,sizeof(A));
        memset(Next,0,sizeof(Next));
        memset(fir,0,sizeof(fir));
        memset(show,0,sizeof(show));
        scanf("%d",&M);
        for(int i=1;i<=N;i++) scanf("%d",&A[i]);//A原数组 
        for(int i=1;i<=N;i++) A[N+i]=A[i];//A原数组 
        N=N*2;
        for(int i=N;i>=1;i--)
        {
            if(!show[A[i]])//show没出现过 
            {
                show[A[i]] = i;
                fir[i] = true;//当前位置的数是第一次出现 
            }
            else
            {//show出现过 
                Next[i] = show[A[i]];//记录他下一次出现的位置 
                fir[Next[i]] = false;//下一个位置不是第一次出现 
                fir[i] = true;//这个位置是第一次出现
                show[A[i]] = i;//记录出现位置
            }
        }
     
        for(int i=1;i<=M;i++)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            q[i].l=r;
            q[i].r=l+N/2;
//          cout<<q[i].l<<q[i].r<<endl;
            q[i].pos = i;
        }
        sort(q+1,q+1+M,cmp);
     
     
        for(int i=1;i<=N;i++)
            if(fir[i])//如果这个数是第一次出现 
            {
                modify(i,1);//初始化
            }
        int qtemp = q[1].l;//当前位置 
        int ptr = 1;//下标位置 
        for(int i=1;i<=M;i++)
        {
            for(;ptr<q[i].l;ptr++)
            {
                if(fir[ptr])//如果第一次出现 
                {
                    modify(ptr,-1); 
                    fir[ptr] = false;//下标在l之前不在区间内 
                    if(Next[ptr])//如果后面还有重复 
                    {
                        fir[Next[ptr]] = true;//下一个是第一次出现 
                        modify(Next[ptr],1);//这个位置的前一个位置次数
                    }
                }
            }
            ptr = q[i].l;
            qtemp = q[i].l;
            res[q[i].pos] = sum(q[i].r) - sum(q[i].l-1);
        }
        for(int i=1;i<=M;i++) printf("%d\n",res[i]);
    }
    return 0;
}
​

猜你喜欢

转载自blog.csdn.net/KeeeepGO/article/details/81127691
今日推荐