BZOJ 2724: [Violet 6]蒲公英
题目大意:
求区间众数,强制在线。
题解:
考虑分块,一段区间的众数一定在整块的众数和两边多出来的数中。
可能是众数的数有O(sqrt(n))个,然后我们考虑查询这些数在区间中出现了几次。
把原来的序列中相同数字的下标从小到大扔进vector中,然后二分就能求每个数在区间中出现了几次。
找一个出现次数最大的就行了。
复杂度O(nsqrt(n)log(n))
代码
#include<cstdio> #include<algorithm> #include<cstring> #include<vector> #include<map> using namespace std; int id,n,m,block,ans,f[505][505],cnt[100005],v[100005],belong[1000005],val[1000005]; map<int,int> mp; vector<int> vec[1000005]; void pre(int x){ memset(cnt,0,sizeof(cnt)); int maxx=0,ans=0; for (int i=(x-1)*block+1; i<=n; i++){ cnt[v[i]]++; int t=belong[i]; if (cnt[v[i]]>maxx || (cnt[v[i]]==maxx && val[v[i]]<val[ans])){ ans=v[i]; maxx=cnt[v[i]]; } f[x][t]=ans; } } int query(int l,int r,int x){ int t=upper_bound(vec[x].begin(),vec[x].end(),r)-lower_bound(vec[x].begin(),vec[x].end(),l); return t; } int calc(int l,int r){ int ans=f[belong[l]+1][belong[r]-1]; int maxx=query(l,r,ans); for (int i=l; i<=min(belong[l]*block,r); i++){ int x=query(l,r,v[i]); if (x>maxx || (x==maxx && val[v[i]]<val[ans])) { ans=v[i]; maxx=x; } } if (belong[l]!=belong[r]){ for (int i=(belong[r]-1)*block+1; i<=r; i++){ int x=query(l,r,v[i]); if (x>maxx || (x==maxx && val[v[i]]<val[ans])) { ans=v[i]; maxx=x; } } } return ans; } int main(){ scanf("%d%d",&n,&m); block=200; int ans=0; for (int i=1; i<=n; i++){ scanf("%d",&v[i]); if (!mp[v[i]]){ mp[v[i]]=++id; val[id]=v[i]; } v[i]=mp[v[i]]; vec[v[i]].push_back(i); } for (int i=1; i<=n; i++) belong[i]=(i-1)/block+1; int cnt=(n-1)/block+1; for (int i=1; i<=cnt; i++) pre(i); for (int i=1; i<=m; i++){ 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); ans=val[calc(l,r)]; printf("%d\n",ans); } return 0; }
: