1. 程式人生 > >【模板】莫隊演算法

【模板】莫隊演算法

題意:給定一個大小為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; }