Little Elephant and Array(扫描线+树状数组)

题目

题目链接

题目大意

给出一个长度为n的序列,进行m次询问。

每次询问区间[ l ,r ]内,有多少个数字x刚好出现了x次。

题目思路

1.这个题是可以用莫队做的

#include<iostream>
#include<algorithm>
#include<map>
#include<math.h> 
using namespace std;
const int maxn=1e6+100;
int a[maxn],b[maxn],c[maxn];
int belong[maxn],res[maxn];
struct node{
    int l,r,id;
}q[maxn]; 
int ans=0;
int mp[maxn];
int cnt=0;
bool cmp(node x,node y){
    if(belong[x.l]!=belong[y.l]){
        return x.l<y.l;
    }
    else{
        if(belong[x.l]&1){
            return x.r<y.r;
        }
        else{
            return x.r>y.r;
        }
    }
}
void add(int pos){
    if(mp[a[pos]]==c[pos]){
        ans--;
    }
    mp[a[pos]]++;
    if(mp[a[pos]]==c[pos]){
        ans++;
    }
}
void remove(int pos){
    if(mp[a[pos]]==c[pos]){
        ans--;
    }
    mp[a[pos]]--;
    if(mp[a[pos]]==c[pos]){
        ans++;
    }
} 
int main(){
    int n,m;
    cin>>n>>m;
    int block=(int)sqrt(n);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i]=c[i]=a[i];
        belong[i]=(i-1)/block+1;
    }
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(b+1,b+n+1,a[i])-b;
    }
    for(int i=1;i<=m;i++){
        cin>>q[i].l>>q[i].r;
        q[i].id=i;
    } 
    sort(q+1,q+m+1,cmp);
    for(int l=1,r=0,i=1;i<=m;i++){
        int ql=q[i].l,qr=q[i].r;
        while(l<ql){
            remove(l++);
        }    
        while(l>ql){
            add(--l);
        } 
        while(r>qr){
            remove(r--);
        } 
        while(r<qr){
            add(++r);
        }
        res[q[i].id]=ans;
    }
    for(int i=1;i<=m;i++){
        cout<<res[i]<<endl;
    }
}
View Code

 

2.扫描线+树状数组

首先呢我们可以把[li,ri]给离线出来,假如询问是[li,ri]的然后我们可以用一个vector给它存起来,就是把ri存起来就是v[y].push({x,id}),这里相当于降维,然后从前向后扫描数组

对于每一个y,求出来[1,r]的区间里的全部贡献,转化为res[x.second] = query(i)-query(x.first-1);,

对于这个题

所以以区间[ 2 , 2 , 2 , 2 ] 为例:

1.r = 1 ,左端点的贡献分别为:[ 0 , 0 , 0 , 0 ]
2. r = 2 ,左端点的贡献分别为:[ 1 , 0 , 0 , 0 ]     这里r=2你只需要ri=2,然后li是小于等与2的,这样区间【1,2】和就是1
3. r = 3 ,左端点的贡献分别为:[ − 1 , 1 , 0 , 0 ]  这里r=3,你只需要看ri=3,li可以是【1,2,3】看看如果是1,前缀和就是0。如果是2,前缀和就是1。如果是3就是0
4. r = 4 ,左端点的贡献分别为:[ 0 , − 1 , 1 , 0 ] 这个和上一个一样分析,然后找出这个数组的变化规律来
记住你看i的时候一定要记得一个点是固定的就是这个ri,然后再看[1.ri]的影响,从而改变数组

#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+100;
typedef long long ll;
typedef pair<int,int>PII;
int a[maxn];
int c[maxn];
int n,m;
int lowbit(int x){
    return x&-x;
}
void add(int x,int val){
    for(int i=x;i<=n;i+=lowbit(i)){
        c[i]+=val; 
    }
}
ll query(int x){
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i)){
        ans+=c[i];
    }
    return ans;
}
vector<PII>q[maxn];
ll ans[maxn];
multiset<int>s[maxn];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];    
    } 
    int x,y;
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        if(x>y){
            swap(x,y);
        }
        q[y].push_back({x,i});
    }
    for(int i=1;i<=n;i++){
        if(a[i]<=n){
            s[a[i]].insert(i);
            int sz=s[a[i]].size();
            if(sz==a[i]){
                int sp=*s[a[i]].begin();
                add(sp,1); 
            }
            else if(sz==a[i]+1){
                int sp=*s[a[i]].begin(),tp=*next(s[a[i]].begin());
                add(sp,-2);
                add(tp,1); 
            }
            else if(sz==a[i]+2){
                int sp=*s[a[i]].begin(),tp=*next(s[a[i]].begin()),tpp=*next(next(s[a[i]].begin()));
                add(sp,1);
                add(tp,-2);
                add(tpp,1);
                s[a[i]].erase(sp);
            }
        }
        for(auto x:q[i]){
            ans[x.second]=query(i)-query(x.first-1);
        }
    }
    for(int i=1;i<=m;i++){
        printf("%lld\n",ans[i]);
    }
    return 0; 
}
View Code

 

posted @ 2021-08-10 20:05  lipu123  阅读(45)  评论(0)    收藏  举报