【模板】莫队算法

题意:给定一个大小为N的数组,数组中所有元素的大小<=N。你需要回答M个查询。每个查询的形式是L,R,K。你需要回答在范围[ L,R ]中至少重复K次的数字的个数。N,M<=100000
诶,这题卡了好久,TLE,中间弃了一段,然后今天学弟学莫队,拿出这个题,他也没什么想法,然后我顿时退一步海阔天空了。
最开始的想法是:莫队排序,当前区间[l,r]->[l+1,r],修改一个点,有两个点的cnt变化(cnt[i]表示出现次数为i的数的个数),由于k不同,动态维护所有的k,然后求前缀和。想到树状数组。然而莫队+树状数组修改O(Msqrt(N)logN) TLE,问了学长,学长blabla说了个用平衡树的,同样超时。然后弃了。今天想了想,发现,区间求和还有另一种嘛,树状数组修改log查询log,块状数组修改O(1),查询O(sqrt(N))啊。瞬间解决了。。
交了之后看到网站上还有别的神犇的做法更简洁,考虑到求至少出现k次的,那么出现x次的之前必有出现x-1次的状态。就是相当于我们不做-1的操作直接+1,这样最后答案直接是ans了。好神啊。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 100005
struct Q{
    int l,r,k,ans,id;
}q[N];
int n,m,a[N],num[N],cnt[N],block[N],tot[N],End[N],Begin[N];
using namespace std;
int read(){
    char ch=getchar();int f=1,x=0;
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
bool cmp(Q x,Q y){
    return block[x.l]<block[y.l]||(block[x.l]==block[y.l]&&x.r<y.r);
}
bool cmp2(Q x,Q y){
    return x.id<y.id;
}
void upd(int pos,int x){
    if(num[a[pos]]==0&&x==-1) return;
    if(num[a[pos]]!=0){
        cnt[num[a[pos]]]--; //a : cnt;block : block
        tot[block[num[a[pos]]]]--;
    }
    num[a[pos]]+=x;
    if(num[a[pos]]!=0){
        cnt[num[a[pos]]]++;
        tot[block[num[a[pos]]]]++;
    }
}
int getsum(int k){
    int tot_sz=0,tmp=1,ret=0;
    while(tot_sz+End[tmp]-Begin[tmp]+1<=k) ret+=tot[tmp],tot_sz+=End[tmp]-Begin[tmp]+1,tmp++;
    for(int i=Begin[tmp];i<=k;i++) ret+=cnt[i];
    return ret;
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    int b=(int)sqrt(n);
    for(int i=1;i<=n;i++) {
        block[i]=(i-1)/b+1;
        if(block[i]!=block[i-1])
            End[block[i-1]]=i-1,Begin[block[i]]=i;
    }End[block[n]]=n;Begin[block[n]+1]=n+1;End[block[n]+1]=n+1;
    for(int i=1;i<=m;i++){
        q[i].l=read();
        q[i].r=read();
        q[i].k=read();
        q[i].id=i;
    }
    sort(q+1,q+1+m,cmp);
    int l=0,r=0;
    for(int i=1;i<=m;i++){
        for(;r<q[i].r;r++) upd(r+1,1);
        for(;r>q[i].r;r--) upd(r,-1);
        for(;l>q[i].l;l--) upd(l-1,1);
        for(;l<q[i].l;l++) upd(l,-1);
        q[i].ans=getsum(r-l+1)-getsum(q[i].k-1);
    }
    sort(q+1,q+1+m,cmp2);
    for(int i=1;i<=m;i++) printf("%d\n",q[i].ans);
    return 0;
}
发布了87 篇原创文章 · 获赞 7 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/yxr0105/article/details/51464799
今日推荐