【演算法學習】莫隊

sad_lin發表於2024-10-29

大佬的部落格

開幕雷擊!我既然都貼上了大佬的部落格,那還要我有什麼用,但是我要是不記錄的話早晚會忘,那既然是給自己看的話就象徵性地寫一下吧,等以後熟練了也不需要這部落格了。

莫隊雖然用到了分塊,但是並不對分塊進行操作,而是對分塊的性質進行運用。

普通莫隊

P1972 [SDOI2009] HH的項鍊

非常模板的題,但是卡莫隊,所以自己去討論區找雙倍經驗吧。

莫隊是怎麼一回事呢,我們把所有的詢問都離線地儲存下來,然後用 \(l,r\) 指標在數列上移動,在移動的過程中處理答案,在指標移動到查詢區間時就儲存當前答案,而這個查詢區間的選擇就很重要了,我們講每個端點都分到不同的塊內,對所屬的塊進行左端點排序,這樣複雜度就降到了 \(O(n\sqrt{n})\)。(我不想證了 其實是不會

最佳化

排序奇偶性最佳化,當查詢區間左端點在同一塊內,判斷所在的塊是奇數就按右端點從小到大排序,是偶數就按從大到小排序,這樣的話相當於是一個來回,可以很好地節約時間。

image

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+19;

int n,m;
int a[N];
int of[N];
int len;
int ans[N];
int cnt[N];
int now=0;
struct ss{
	int l,r,id;
}q[N];
bool cmp(ss g,ss h){
	return (of[g.l]^of[h.l])?of[g.l]<of[h.l]:(of[g.l]&1)?g.r<h.r:g.r>h.r;
}
void add(int pos){
	if(!cnt[a[pos]]++) ++now;
}
signed main(){
    ios::sync_with_stdio(false);
	cin>>n>>m;
	len=sqrt(n);
	for(int i=1;i<=n;i++){
		of[i]=(i-1)/len+1;
	}
	
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	
	for(int i=1;i<=m;i++){
		cin>>q[i].l>>q[i].r;
		q[i].id=i;
	}
	
	sort(q+1,q+1+m,cmp); 
	
	int l=1,r=0;
	
	for(int i=1;i<=m;i++){
		int ql=q[i].l,pr=q[i].r;
		
		while(l<ql) now-=!--cnt[a[l++]];
		while(l>ql) now+=!cnt[a[--l]]++;
		while(r<pr) now+=!cnt[a[++r]]++;
		while(r>pr) now-=!--cnt[a[r--]];
		
		ans[q[i].id]=now;
	}
	
	for(int i=1;i<=m;i++){
		cout<<ans[i]<<"\n";
	}
	
    return 0;
}

帶修莫隊

相關文章