CH 4401 - 蒲公英 - [分块]
Posted dilthey
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CH 4401 - 蒲公英 - [分块]相关的知识,希望对你有一定的参考价值。
题目链接:传送门
题解:
经典的在线求区间众数的问题,由于区间众数不满足区间可加性,所以考虑分块,假设分块长度为 $S$,则总共分成 $T=N/S$ 块,
对于每个询问 $[l,r]$,设点 $l$ 在第 $p$ 块,点 $r$ 在第 $q$ 块,假设第 $p+1$ 到第 $q-1$ 块这整一个区间为 $[L,R]$,
那么,查询的区间就被分为 $[l,L)$ 和 $[L,R]$ 和 $(R,r]$ 三大块,显然可以分两种情况讨论:
1、$[L,R]$ 这个区间的众数就是 $[l,r]$ 的众数;
2、$[L,R]$ 这个区间的众数不是 $[l,r]$ 的众数,那么必然是由于 $[L,R]$ 区间内的某个数,它出现的次数,加上了 $[l,L)$ 和 $(R,r]$ 中出现的次数,超过了原本 $[L,R]$ 的众数;因此,这个必然在 $[l,L)$ 和 $(R,r]$ 中出现。
这样一来,就很好算了,不妨对于所有可行的 $[L,R]$,预处理出一个数组 $cnt_{L,R}$,记录区间 $[L,R]$ 内每个数字出现的次数,同时再记录 $[L,R]$ 的众数是哪个,
显然经过离散化处理后,$cnt_{L,R}$ 的空间复杂度为 $O(N)$,而所有可行的区间 $[L,R]$ 有 $O(T^2) = O(N^2 /S^2)$ 个;
那么,对于每次查询 $[l,r]$,$O(S)$ 枚举 $[l,L)$ 和 $(R,r]$ 中的出现的数,把它们加到 $[L,R]$ 对应的 $cnt_{L,R}$ 数组之中,维护最大值的同时与 $[L,R]$ 的众数的出现次数进行比较,就可以找到众数,
最后,再 $O(S)$ 地枚举 $[l,L)$ 和 $(R,r]$ 中的出现的数,把 $cnt_{L,R}$ 数组复原即可。
因此,对于每次查询,$O(S)$ 即可求得答案,总的就是 $O(MS)$;而预处理是 $O(NT^2) = O(N^3 /S^2)$;所以总时间复杂度为 $O(MS + N^3 /S^2)$,可知,当 $S = sqrt[3]{{N^3 /M}} = frac{N}{{sqrt[3]{M}}}$ 时最小,为 $O(NM^{frac{2}{3}})$。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef pair<int,int> pii; const int maxn=4e4+10; const int maxm=5e4+10; int n,m; int a[maxn],aid[maxn]; int cnt[40][40][maxn]; pii mode[40][40]; int block[maxn],len,tot; int L[maxn],R[maxn]; vector<int> v; inline int getid(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;} void update(int x,int y,int i,pii &res) { cnt[x][y][aid[i]]++; if(cnt[x][y][aid[i]]>res.first || (cnt[x][y][aid[i]]==res.first && a[i]<a[res.second])) { res.first=cnt[x][y][aid[i]]; res.second=i; } } int ask(int l,int r) { int st=block[l],ed=block[r]; pii res; if(ed-st<=1) { res=make_pair(0,0); for(int i=l;i<=r;i++) update(0,0,i,res); for(int i=l;i<=r;i++) cnt[0][0][aid[i]]--; } else { res=mode[st+1][ed-1]; for(int i=l;i<=R[st];i++) update(st+1,ed-1,i,res); for(int i=L[ed];i<=r;i++) update(st+1,ed-1,i,res); for(int i=l;i<=R[st];i++) cnt[st+1][ed-1][aid[i]]--; for(int i=L[ed];i<=r;i++) cnt[st+1][ed-1][aid[i]]--; } return res.second; } int main() { cin>>n>>m; len=max(1,(int)(n/pow(m,1.0/3.0))); for(int i=1;i<=n;i++) { cin>>a[i]; v.push_back(a[i]); block[i]=(i-1)/len+1; if(i==1) L[block[i]]=i; if(i==n) R[tot=block[i]]=i; if(2<=i && i<=n && block[i-1]!=block[i]) { R[block[i-1]]=i-1; L[block[i]]=i; } } sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end()); for(int i=1;i<=n;i++) aid[i]=getid(a[i]); memset(cnt,0,sizeof(cnt)); for(int i=1;i<=tot;i++) { for(int j=i;j<=tot;j++) { mode[i][j]=make_pair(0,0); for(int k=L[i];k<=R[j];k++) { cnt[i][j][aid[k]]++; if(cnt[i][j][aid[k]]>mode[i][j].first || (cnt[i][j][aid[k]]==mode[i][j].first && a[k]<a[mode[i][j].second])) { mode[i][j].first=cnt[i][j][aid[k]]; mode[i][j].second=k; } } } } int ans=0; while(m--) { int l,r; scanf("%d%d",&l,&r); l=(l+ans-1)%n+1; r=(r+ans-1)%n+1; if(l>r) swap(l,r); printf("%d ",ans=a[ask(l,r)]); } }
(被这题僵了好久……深深感到自己码力的弱小……)
以上是关于CH 4401 - 蒲公英 - [分块]的主要内容,如果未能解决你的问题,请参考以下文章