回滾莫隊
阿新 • • 發佈:2021-08-11
實現增加不可實現或者只有刪除不可實現的莫隊。
回滾會 \(L\) 移動之前的答案,並且將 \(L\) 重新設為下一塊的起點。可以證明覆雜度仍然是 \(\mathcal O(n^{1.5})\) 級別。
聽 jmr 講資料結構聽到自閉,於是痛定思痛決定補一補技能點。
主要參考 ouuan 的部落格。
演算法流程
同普通莫隊,對詢問端點進行分塊,對於左右端點在同一塊的詢問直接暴力計算。
bool cmp(Query a,Query b){return a.bl!=b.bl?a.l<b.l:a.r<b.r;}
排序之後,詢問左端點 \(l\) 換塊時清空答案,這時對於這一塊的詢問右端點 \(r\) 是單調遞增的。對於同一塊內的詢問,先將莫隊的 \(L\) 設為下一塊的起始點,然後將莫隊的 \(R\) 向右移動至 \(r\),然後 \(L\) 移動至 \(l\)。此次詢問處理完之後將 \(ans\)
例題
LOJ#2874. 「JOISC 2014 Day1」歷史研究
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<stack> #include<set> #include<map> #include<vector> #include<ctime> #include<cstdlib> using namespace std; #define mp make_pair #define pb push_back typedef pair<int,int> pii; typedef long long ll; typedef unsigned long long ull; inline int read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return x*f; } const int N=1e5+10; int cnt[N],t[N],a[N]; ll ans,Ans[N]; void add(int x) { cnt[x]++; ans=max(ans,1ll*t[x]*cnt[x]); } struct Query{int l,r,pos,bl;}q[N]; bool cmp(Query x,Query y){return x.bl==y.bl?x.r<y.r:x.l<y.l;} int main() { int n=read(),m=read(),sz=sqrt(n),cnt1=0; for(int i=1;i<=n;i++)a[i]=t[i]=read(); int c=n;sort(t+1,t+c+1),c=unique(t+1,t+c+1)-t-1; for(int i=1;i<=n;i++)a[i]=lower_bound(t+1,t+c+1,a[i])-t; for(int i=1;i<=m;i++) { int l=read(),r=read(); if(l/sz==r/sz) { ans=0; for(int j=l;j<=r;j++)cnt[a[j]]++,ans=max(ans,1ll*cnt[a[j]]*t[a[j]]); Ans[i]=ans; ans=0; for(int j=l;j<=r;j++)cnt[a[j]]=0; } else q[++cnt1]=Query{l,r,i,l/sz}; } sort(q+1,q+cnt1+1,cmp); for(int i=1,L=1,R=0;i<=cnt1;i++) { // printf("[%d, %d]\n",q[i].l,q[i].r); if(q[i].bl!=q[i-1].bl||i==1) { memset(cnt,0,sizeof(cnt)); L=(q[i].bl+1)*sz; R=(q[i].bl+1)*sz-1; ans=0; } while(R<q[i].r)add(a[++R]); ll tmp=ans; while(L>q[i].l)add(a[--L]); Ans[q[i].pos]=ans; while(L<(q[i].bl+1)*sz)cnt[a[L]]--,L++; ans=tmp; } for(int i=1;i<=m;i++)printf("%lld\n",Ans[i]); return 0; }