【模板】莫隊演算法
阿新 • • 發佈:2019-01-30
題意:給定一個大小為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;
}