帶修莫隊學習筆記
阿新 • • 發佈:2020-10-21
目錄
-1.參考資料
無
0.定義
無
1.演算法
1.1 普通莫隊
對左端點分塊,然後把詢問按照塊的編號排序,相同塊內按照右端點排序。
然後考慮兩次詢問之間轉移的變化量,就是左端點移動+右端點移動,直接類似於 two pointers 即可。可以證明取塊長為 \(O(\sqrt n)\) 時可以達到理論最優複雜度是 \(O(n\sqrt nf(n))\) 的,其中 \(f(n)\) 是普通莫隊的複雜度。
1.2 帶修莫隊
簡單來說就是多了個時間軸的三維莫隊。
考慮沿用普通莫隊的想法,對左端點和右端點都分塊,然後把詢問按照先左端點塊編號後右端點塊編號排序,相同部分按照時間排序。
然後考慮兩次詢問之間轉移的變化量,是左端點移動+右端點移動+時間的改變,同理上面操作即可。three pointers。
可以證明取塊長為 \(O((nq)^\frac{1}{3})\sim O(n^\frac{2}{3})\) 時可以達到理論最優複雜度是 \(O(n^\frac{4}{3}q^\frac{1}{3})\sim O(n^\frac{5}{3})\),其中 \(q\) 表示修改個數,一般認為 \(q\sim n\)。
1.3 例題
直接跑帶修莫隊即可。
Code
#include<bits/stdc++.h> using namespace std; const int N=133335,M=1000005; int n,m,a[N],cnt[M],bl,uv,cntr,cntq,ans[N]; char op[2]; struct query{ int id,l,r,t,posl,posr; bool operator<(const query&x)const{ return posl<x.posl||posl==x.posl&&posr<x.posr||posl==x.posl&&posr==x.posr&&t<x.t; } }q[N]; int x[N],c[N]; void add(int x){if((cnt[x]++)==0)uv++;} void del(int x){if((--cnt[x])==0)uv--;} void upd(int id,int t){ if(q[id].l<=x[t]&&x[t]<=q[id].r)del(a[x[t]]),add(c[t]); swap(a[x[t]],c[t]); } int main(){ scanf("%d%d",&n,&m);bl=pow(n,0.667); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=m;i++){ scanf("%s",op); if(op[0]=='R')cntr++,scanf("%d%d",&x[cntr],&c[cntr]); else { cntq++; scanf("%d%d",&q[cntq].l,&q[cntq].r); q[cntq].id=cntq,q[cntq].t=cntr; q[cntq].posl=q[cntq].l/bl,q[cntq].posr=q[cntq].r/bl; } } sort(q+1,q+cntq+1); for(int i=1,l=1,r=0,t=0;i<=cntq;i++){ while(l>q[i].l)add(a[--l]); while(r<q[i].r)add(a[++r]); while(l<q[i].l)del(a[l++]); while(r>q[i].r)del(a[r--]); while(t<q[i].t)upd(i,++t); while(t>q[i].t)upd(i,t--); ans[q[i].id]=uv; } for(int i=1;i<=cntq;i++)printf("%d\n",ans[i]); return 0; }