牛客网暑期ACM多校训练营(第一场)-> J Different Integers (离线莫队+树状数组)

J Different Integers

Given a sequence of integers a 1 , a 2 , …, a n and q pairs of integers (l 1 , r 1 ), (l 2 , r 2 ), …, (l q , r q ), find count(l 1 , r 1 ),count(l 2 , r 2 ), …, count(l q , r q ) where count(i, j) is the number of different integers among a 1 , a 2 , …, a i , a j , a j + 1 ,…, a n .

输入描述:

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 , a 2 , …, a n .
The i-th of the following q lines contains two integers l i and r i .

输出描述:

For each test case, print q integers which denote the result.
备注
* 1 ≤ n, q ≤ 10 5
* 1 ≤ a i ≤ n
* 1 ≤ l i , r i ≤ 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

题解:这题听直播题解的标准解法是树状数组,看了给的题解很久还是看不太懂,大佬的思想很活,之后看了别人2*n的内存空间的题解,得知原来可以这样:将a【i+n】=a【i】,就可以将1-i,j-n这样不连续的区间并集转化为连续的区间求并集,再用树状数组模板解即可。

#include<cstdio>
#include<map>
#include<algorithm> 
using namespace std;

const int maxn=200000+100;  ///括大一倍

int bit[maxn];  //树节点数组 
int a[maxn];    //原数组 
int Ans[maxn];  //询问结果 
int n,m;

inline int lowbit(int x){
    return x&(-x);
}

inline void Add(int x,int v){
    while(x<=n){
        bit[x]+=v;
        x+=lowbit(x);
    }
} 

inline int getSum(int x){
    int sum=0;
    while(x>0){
        sum+=bit[x];
        x-=lowbit(x);
    }
    return sum;
}

struct Query{
    int l,r;
    int id;
}query[maxn];

inline bool cmp(Query aa,Query bb){
    return aa.r<bb.r;
}

int main(){
    map<int,int>M;
    while(scanf("%d%d",&n,&m)==2){
        M.clear();
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            a[n+i]=a[i];
            bit[i]=bit[n+i]=0;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d",&query[i].r,&query[i].l);
            query[i].r+=n;
            query[i].id=i; 
        }
        n*=2;//扩大一倍 
        sort(query+1,query+m+1,cmp);
        int pre=1;
        for(int i=1;i<=m;i++){      //建树 
            for(int j=pre;j<=query[i].r;j++){
                if(M[a[j]]!=0){
                    Add(M[a[j]],-1);
                } 
                Add(j,1);
                M[a[j]]=j;//标记 
            }
            pre=query[i].r+1;
            Ans[query[i].id]=getSum(query[i].r)-getSum(query[i].l-1);
        }
        for(int i=1;i<=m;i++) printf("%d\n",Ans[i]);
    }
} 

离线+莫队算法代码:

莫队算法的使用,我们只需要维护每个数出现的频率即可,当出现频率减到0时tmp值减一,当频率从0到1是,tmp值加一.

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=100005;
int q,m;
int s[maxn],vis[maxn],md[maxn],Ans[maxn];

struct Query{
    int lef,rig,id;
}Que[maxn];

bool cmp(struct Query a,struct Query b)
{
    //很重要!!!不然会TLE
    if(md[a.lef]!=md[b.lef])return a.lef<b.lef;//先同一块l排序,不在同一块就r排序
    return a.rig<b.rig;
}

void modui()
{
    int i,lef=0,rig=q+1,ans=0;
    for(i=1;i<=m;i++)
    {
        while(rig<Que[i].rig)
        {
            vis[s[rig]]--;
            if(vis[s[rig]]==0)
                ans--;
            rig++;
        }
        while(rig>Que[i].rig)
        {
            rig--;
            if(vis[s[rig]]==0)
                ans++;
            vis[s[rig]]++;
        }
        while(lef<Que[i].lef)
        {
            lef++;
            if(vis[s[lef]]==0)
                ans++;
            vis[s[lef]]++;
        }
        while(lef>Que[i].lef)
        {
            vis[s[lef]]--;
            if(vis[s[lef]]==0)
                ans--;
            lef--;
        }
        Ans[Que[i].id]=ans;
    }
    return ;
}
int main()
{
    int i,temp;
    while(scanf("%d%d",&q,&m)!=EOF)
    {
        memset(vis,0,sizeof(vis));
        memset(Ans,0,sizeof(Ans));
        temp=sqrt(q);       //分块 
        for(i=1;i<=q;i++)
        {
            scanf("%d",&s[i]);
            md[i]=(i-1)/temp+1;     //所属块 
        }
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&Que[i].lef,&Que[i].rig);
            Que[i].id=i;
        }
        sort(Que+1,Que+m+1,cmp);
        modui();
        for(i=1;i<=m;i++)printf("%d\n",Ans[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wuyileiju__/article/details/81146576