【学习笔记】震惊,全机房都会分块了,就我没有
震惊,蒟蒻 Midoria7 居然在 1949 年终于写完了分块入门,活到爆!
分块是一种很暴力很优雅的数据结构。秉承散点暴力,大块整体的原则,能以根号级别复杂度优化很多的暴力。
以下由于都是隔了很长时间写的,所以码风有可能有变。
数列分块入门 1
区间加法,单点查询。
小点暴力修改,大块打标记。(由于我太懒所以把区间修改区间查询复制过来了)
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e4+10;
int n,S;
int a[maxn];
inline int read(){
int x=0,fopt=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')fopt=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*fopt;
}
int sum[maxn],siz[maxn],L[maxn],R[maxn],belong[maxn],tag[maxn];
inline void Init(){
S=sqrt(n);
for(int i=1;i<=n;i++){
belong[i]=(i-1)/S+1;
siz[belong[i]]++;
sum[belong[i]]+=a[i];
if(!L[belong[i]])L[belong[i]]=i;
R[belong[i]]=i;
}
}
inline void add(int Lu,int Ru,int v){
if(belong[Lu]==belong[Ru]){
sum[belong[Lu]]+=(Ru-Lu+1)*v;
for(int i=Lu;i<=Ru;i++)
a[i]+=v;
}else{
add(Lu,R[belong[Lu]],v);
add(L[belong[Ru]],Ru,v);
for(int i=belong[Lu]+1;i<belong[Ru];i++){
tag[i]+=v;
sum[i]+=siz[i]*v;
}
}
}
inline int query(int Lu,int Ru){
int ans=0;
if(belong[Lu]==belong[Ru]){
ans+=(Ru-Lu+1)*tag[belong[Lu]];
for(int i=Lu;i<=Ru;i++)
ans+=a[i];
}else{
ans+=query(Lu,R[belong[Lu]]);
ans+=query(L[belong[Ru]],Ru);
for(int i=belong[Lu]+1;i<belong[Ru];i++)
ans+=sum[i];
}
return ans;
}
int main(){
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
Init();
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),c=read();
if(opt==0){
add(l,r,c);
}else{
printf("%d\n",query(r,r));
}
}
return 0;
}
数列分块入门 2
区间加法,询问区间内小于某个值的元素个数。
用归并排序维护块内的单调性,查询直接 lower_bound
。散点暴力统计即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e4+10;
int n,S;
int a[maxn],c[maxn];
inline int read(){
int x=0,fopt=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')fopt=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*fopt;
}
inline bool Cmp(int x,int y){
return a[x]<a[y];
}
int sum[maxn],siz[maxn],L[maxn],R[maxn],belong[maxn],tag[maxn];
inline void Init(){
S=sqrt(n);
for(int i=1;i<=n;i++){
belong[i]=(i-1)/S+1;
siz[belong[i]]++;
if(!L[belong[i]])L[belong[i]]=i;
R[belong[i]]=i;
c[i]=i;
}
for(int i=1;i<=belong[n];i++)
sort(c+L[i],c+R[i]+1,Cmp);
}
inline void add(int Lu,int Ru,int v){
if(belong[Lu]==belong[Ru]){
for(int i=Lu;i<=Ru;i++)
a[i]+=v;
vector<int> A,B;
for(int i=L[belong[Lu]];i<=R[belong[Lu]];i++)
if(c[i]>=Lu&&c[i]<=Ru)A.push_back(c[i]);
else B.push_back(c[i]);
int p1=L[belong[Lu]],p2=0,p3=0;
while(p2<A.size()&&p3<B.size())
a[A[p2]]<a[B[p3]]?(c[p1++]=A[p2++]):(c[p1++]=B[p3++]);
while(p2<A.size())c[p1++]=A[p2++];
while(p3<B.size())c[p1++]=B[p3++];
}else{
add(Lu,R[belong[Lu]],v);
add(L[belong[Ru]],Ru,v);
for(int i=belong[Lu]+1;i<belong[Ru];i++){
tag[i]+=v;
}
}
}
inline int query(int Lu,int Ru,int v){
int ans=0;
if(belong[Lu]==belong[Ru]){
for(int i=Lu;i<=Ru;i++)
if(a[i]+tag[belong[Lu]]<v)ans++;
}else{
ans+=query(Lu,R[belong[Lu]],v);
ans+=query(L[belong[Ru]],Ru,v);
for(int i=belong[Lu]+1;i<belong[Ru];i++){
a[0]=v-tag[i];
ans+=lower_bound(c+L[i],c+R[i]+1,0,Cmp)-c-L[i];
}
}
return ans;
}
signed main(){
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
Init();
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),x=read();
if(opt==0){
add(l,r,x);
}else{
printf("%lld\n",query(l,r,x*x));
}
}
return 0;
}
数列分块入门 3
区间加法,查找前驱。
震惊,这不是 set
吗。大块直接 lower_bound
,修改就先删掉,再插入即可。散点依然暴力找。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+10;
int n,S;
int a[maxn],belong[maxn],tag[maxn];
set<int> tree[1000+10];
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
inline void modify(int l,int r,int c){
for(int i=l;i<=min(belong[l]*S,r);i++){
tree[belong[l]].erase(a[i]);
a[i]+=c;
tree[belong[l]].insert(a[i]);//先删后加
}
if(belong[l]!=belong[r]){
for(int i=(belong[r]-1)*S+1;i<=r;i++){
tree[belong[r]].erase(a[i]);
a[i]+=c;
tree[belong[r]].insert(a[i]);
}
}
for(int i=belong[l]+1;i<belong[r];i++)
tag[i]+=c;
}
inline int Findsucc(int l,int r,int c){
int ans=-1;
for(int i=l;i<=min(belong[l]*S,r);i++){
int sum=a[i]+tag[belong[l]];
if(sum<c)ans=max(ans,sum);
}
if(belong[l]!=belong[r]){
for(int i=(belong[r]-1)*S+1;i<=r;i++){
int sum=a[i]+tag[belong[r]];
if(sum<c)ans=max(ans,sum);
}
}
for(int i=belong[l]+1;i<belong[r];i++){
int temp=c-tag[i];
set<int>::iterator it=tree[i].lower_bound(temp);
if(it==tree[i].begin())continue;
//因为lower_bound会指向大于等于的数,所以前驱就--it即可。
it--;
ans=max(ans,*it+tag[i]);
}
return ans;
}
signed main(){
n=read();S=sqrt(n);
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++){
belong[i]=(i-1)/S+1;
tree[belong[i]].insert(a[i]);
}
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),c=read();
if(opt==0){
modify(l,r,c);
}else{
printf("%lld\n",Findsucc(l,r,c));
}
}
return 0;
}
数列分块入门 4
区间加法,区间查询。
纯板子,不知道为什么放在第 4 个。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e4+10;
int n,S;
int a[maxn];
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
int sum[maxn],L[maxn],R[maxn],belong[maxn],siz[maxn],tag[maxn];
inline void Init(){
S=sqrt(n);
for(int i=1;i<=n;i++){
belong[i]=(i-1)/S+1;
siz[belong[i]]++;
sum[belong[i]]+=a[i];
if(!L[belong[i]])L[belong[i]]=i;
R[belong[i]]=i;
}
}
void add(int s,int t,int v){
if(belong[s]==belong[t]){
sum[belong[s]]+=(t-s+1)*v;
for(int i=s;i<=t;i++)
a[i]+=v;
}else{
add(s,R[belong[s]],v);
add(L[belong[t]],t,v);
for(int i=belong[s]+1;i<belong[t];i++){
tag[i]+=v;
sum[i]+=siz[i]*v;
}
}
}
int query(int s,int t){
int ans=0;
if(belong[s]==belong[t]){
ans+=tag[belong[s]]*(t-s+1);
for(int i=s;i<=t;i++)
ans+=a[i];
}else{
ans+=query(s,R[belong[s]]);
ans+=query(L[belong[t]],t);
for(int i=belong[s]+1;i<belong[t];i++)
ans+=sum[i];
}
return ans;
}
signed main(){
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),c=read();
if(opt==0){
add(l,r,c);
}else{
printf("%lld\n",query(l,r)%(c+1));
}
}
return 0;
}
数列分块入门 5
区间开方,区间求和。
开始不板子了。开方并不具有合并性,但是我们发现即使暴力开方 \(2^31\),在开方五次后都接近 \(1\) 了。所以当一个块内的值都小于等于 \(1\) 的时候(因为有向下取整可能出 \(0\))的时候,开方也没什么意义了。
所以我们只需要新开一个数组标记一下就好了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e4+10;
int n,S;
int a[maxn];
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
int belong[maxn],sum[maxn],L[maxn],R[maxn];
bool vis[maxn];
inline void Sqrt(int x){
if(vis[x])return;
vis[x]=1;sum[x]=0;
for(int i=L[x];i<=R[x];i++){
a[i]=sqrt(a[i]);
sum[x]+=a[i];
if(a[i]>1)vis[x]=0;
}
}
inline void modify(int l,int r){
if(vis[belong[l]]==0){
for(int i=l;i<=min(R[belong[l]],r);i++){
sum[belong[l]]-=a[i];
a[i]=sqrt(a[i]);
sum[belong[l]]+=a[i];
}
vis[belong[l]]=1;
for(int i=L[belong[l]];i<=R[belong[l]];i++)
if(a[i]>1){
vis[belong[l]]=0;break;
}
}
if(belong[l]!=belong[r]&&vis[belong[r]]==0){
for(int i=L[belong[r]];i<=r;i++){
sum[belong[r]]-=a[i];
a[i]=sqrt(a[i]);
sum[belong[r]]+=a[i];
}
vis[belong[r]]=1;
for(int i=L[belong[r]];i<=R[belong[r]];i++)
if(a[i]>1){
vis[belong[r]]=0;break;
}
}
for(int i=belong[l]+1;i<belong[r];i++)
Sqrt(i);//如果没有全开方完就暴力开方同时更新标记
}
int query(int l,int r){
int ans=0;
for(int i=l;i<=min(R[belong[l]],r);i++)
ans+=a[i];
if(belong[l]!=belong[r]){
for(int i=L[belong[r]];i<=r;i++)
ans+=a[i];
}
for(int i=belong[l]+1;i<belong[r];i++)
ans+=sum[i];
return ans;
}
signed main(){
n=read();S=sqrt(n);
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++){
belong[i]=(i-1)/S+1;
sum[belong[i]]+=a[i];
if(!L[belong[i]])L[belong[i]]=i;
R[belong[i]]=i;
}
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),c=read();
if(l>r)swap(l,r);
if(opt==0){
modify(l,r);
}else{
printf("%lld\n",query(l,r));
}
}
return 0;
}
数列分块入门 6
单点插入,单点询问。
震惊,vector
水过,然后 TLE 70 分。
个人感觉是 9 道题里细节最多的,也是最暴力的。
网上的各种 vector + lower_bound
并没有看懂。所以我直接定义 \(a[i][j]\) 表示第 \(i\) 个块中第 \(j\) 个数,\(len[i]\) 表示第 \(i\) 个块的数的个数。
修改?直接暴力循环找所在的块,直接把 \(r\) 以后的往后挪(这也太暴力了吧)。
但是特殊构造的数据会导致某一个块长度变特别大,然后复杂度就 GG 了。
所以我们发现某一个块长度太大的时候,新建一个 \(b[i][j]\) 重新分块,然后再把 \(b[i][j]\) 复制到 \(a[i][j]\) 里去。
很黄很暴力
思想很好懂。难点是边界的处理,需要大力分类讨论。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+10;
const int maxm=1e3+10;
const int Mod=10007;
int n,m,S,pos;
int a[maxm][maxm],b[maxm][maxm],len[maxn];
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
inline void build(){
int x=0,cnt=m/S,lenth=0;
if(m%S!=0)cnt++;
m+=S;pos=0;S=sqrt(m);//注意最后一个块的大小
for(int i=1;i<=cnt;i++){
for(int j=1;j<=len[i];j++){
x++;
int nowbelong=(x-1)/S+1;
b[nowbelong][++lenth]=a[i][j];
a[i][j]=0;
if(lenth>=S)lenth=0;
}
}
cnt=m/S;
if(m%S==0)cnt++;
for(int i=1;i<cnt;i++){
len[i]=S;
for(int j=1;j<=len[i];j++)
a[i][j]=b[i][j];
}
if(m%S==0)len[cnt]=S;
else len[cnt]=m-S*S;//同上
for(int j=1;j<=len[cnt];j++)
a[cnt][j]=b[cnt][j];
}
signed main(){
n=m=read();S=sqrt(n);
for(int i=1;i<=n;i++){
int x=read();
int nowbelong=(i-1)/S+1;
a[nowbelong][++len[nowbelong]]=x;
}
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),c=read();
if(opt==0){
pos++;
int x=0,j;
for(j=1;j<=S;j++){
if(x+len[j]>=l)break;
else x+=len[j];//暴力查找
}
len[j]++;l-=x;
for(int i=len[j];i>l;i--)
a[j][i]=a[j][i-1];//暴力后移
a[j][l]=r;
if(pos>=S)build();//块太大了,直接重构
}else{
int x=0,j;
for(j=1;j<=S;j++){
if(x+len[j]>=r)break;
else x+=len[j];
}
printf("%lld\n",a[j][r-x]);
}
}
return 0;
}
数列分块入门 7
区间乘法,区间加法,单点询问。
类似线段树的方法,维护两个标记即可。会线段树 2 就会这个。不会有人不会乘法分配律吧不会吧不会吧。
不同的是暴力维护散点的时候要先将标记下放,否则乘法会出问题(显然)。当标记下放之后,一定要把标记清零(乘法标记变成 1)。
由于模数较小,导致可能爆负,所以建议读入的时候就模一下。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+10;
const int Mod=10007;
int n,S;
int a[maxn];
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
int belong[maxn],sum[maxn],L[maxn],R[maxn];
int tagadd[maxn],tagmul[maxn];
void add(int l,int r,int c){
for(int i=L[belong[l]];i<=R[belong[l]];i++){
a[i]=(a[i]*tagmul[belong[l]]%Mod+tagadd[belong[l]]+Mod)%Mod;
if(l<=i&&i<=r)a[i]=(a[i]+c+Mod)%Mod;
}
tagadd[belong[l]]=0;tagmul[belong[l]]=1;
if(belong[l]!=belong[r]){
for(int i=L[belong[r]];i<=R[belong[r]];i++){
a[i]=(a[i]*tagmul[belong[r]]%Mod+tagadd[belong[r]]+Mod)%Mod;
if(i<=r)a[i]=(a[i]+c+Mod)%Mod;
}
}
tagadd[belong[r]]=0;tagmul[belong[r]]=1;
for(int i=belong[l]+1;i<belong[r];i++)
tagadd[i]=(tagadd[i]+c+Mod)%Mod;
}
void mul(int l,int r,int c){
for(int i=L[belong[l]];i<=R[belong[l]];i++){
a[i]=(a[i]*tagmul[belong[l]]%Mod+tagadd[belong[l]]+Mod)%Mod;
if(l<=i&&i<=r)a[i]=(a[i]*c+Mod)%Mod;
}
tagadd[belong[l]]=0;tagmul[belong[l]]=1;
if(belong[l]!=belong[r]){
for(int i=L[belong[r]];i<=R[belong[r]];i++){
a[i]=(a[i]*tagmul[belong[r]]%Mod+tagadd[belong[r]]+Mod)%Mod;
if(i<=r)a[i]=(a[i]*c+Mod)%Mod;
}
}
tagadd[belong[r]]=0;tagmul[belong[r]]=1;
for(int i=belong[l]+1;i<belong[r];i++){
tagadd[i]=(tagadd[i]*c+Mod)%Mod;//加法标记也要乘c
tagmul[i]=(tagmul[i]*c+Mod)%Mod;
}
}
signed main(){
n=read();S=sqrt(n);
for(int i=1;i<=n;i++)
a[i]=read()%Mod;
for(int i=1;i<=n;i++){
belong[i]=(i-1)/S+1;
if(!L[belong[i]])L[belong[i]]=i;
R[belong[i]]=i;
tagadd[belong[i]]=0;
tagmul[belong[i]]=1;
}
for(int i=1;i<=n;i++){
int opt=read(),l=read(),r=read(),c=read()%Mod;
if(l>r)swap(l,r);
if(opt==0){
add(l,r,c);
}else if(opt==1){
mul(l,r,c);
}else{
printf("%lld\n",(a[r]*tagmul[belong[r]]%Mod+tagadd[belong[r]])%Mod);
}
}
return 0;
}
数列分块入门 8
操作涉及区间询问等于一个数 \(c\) 的元素,并将这个区间的所有元素改为 \(c\)。
区间推平,震惊,珂朵莉树
应该是这几道题里码量最大的(不过如此)。
我们维护的标记要有以下几种情况:
块内值不相等(INF),块内值相等但不是 \(c\)(相等的那个数),块内值相等且都是 \(c\)(\(c\))。
注意 hzwer 的标程中第一种情况是 -1,不过我感觉可能 \(c=-1\),所以就改成 INF 了。
INF 的话,直接暴力扫一遍统计更新就可。最后还要统计一下是否更新标记。
第二种情况只需要更新,还是小块暴力改,大块改标记。
第三种情况直接更新答案了,不用更新。
大力分类讨论。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
int n,S;
int a[maxn];
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
int belong[maxn],L[maxn],R[maxn],v[maxn];
inline void Solve(int l,int r,int c){
int ans=0;
if(belong[l]==belong[r]){
if(v[belong[l]]==c)ans+=r-l+1;
else{
if(v[belong[l]]!=INF){
for(int i=L[belong[l]];i<=R[belong[l]];i++)
a[i]=v[belong[l]];
}
for(int i=l;i<=r;i++)
if(a[i]==c)ans++;
else a[i]=c;
bool flag=0;
for(int i=L[belong[l]];i<=R[belong[l]];i++)
if(a[i]!=c){
flag=1;break;
}
if(flag)v[belong[l]]=INF;
else v[belong[l]]=c;
}
}else{
if(v[belong[l]]==c)ans+=R[belong[l]]-l+1;
else{
if(v[belong[l]]!=INF){
for(int i=L[belong[l]];i<=R[belong[l]];i++)
a[i]=v[belong[l]];
}
for(int i=l;i<=R[belong[l]];i++)
if(a[i]==c)ans++;
else a[i]=c;
bool flag=0;
for(int i=L[belong[l]];i<=R[belong[l]];i++)
if(a[i]!=c){
flag=1;break;
}
if(flag)v[belong[l]]=INF;
else v[belong[l]]=c;
}
if(v[belong[r]]==c)ans+=r-L[belong[r]]+1;
else{
if(v[belong[r]]!=INF){
for(int i=L[belong[r]];i<=R[belong[r]];i++)
a[i]=v[belong[r]];
}
for(int i=L[belong[r]];i<=r;i++)
if(a[i]==c)ans++;
else a[i]=c;
bool flag=0;
for(int i=L[belong[r]];i<=R[belong[r]];i++)
if(a[i]!=c){
flag=1;break;
}
if(flag)v[belong[r]]=INF;
else v[belong[r]]=c;
}
for(int i=belong[l]+1;i<belong[r];i++){
if(v[i]==c)ans+=S;
else{
if(v[i]!=INF)v[i]=c;
else{
for(int j=L[i];j<=R[i];j++)
if(a[j]==c)ans++;
else a[j]=c;
bool flag=0;
for(int j=L[i];j<=R[i];j++)
if(a[j]!=c){
flag=1;break;
}
if(flag)v[i]=INF;
else v[i]=c;
}
}
}
}
printf("%lld\n",ans);
}
signed main(){
n=read();S=sqrt(n);
for(int i=1;i<=n;i++){
a[i]=read();
belong[i]=(i-1)/S+1;
if(!L[belong[i]])L[belong[i]]=i;
R[belong[i]]=i;
v[i]=INF;
}
for(int i=1;i<=n;i++){
int l=read(),r=read(),c=read();
Solve(l,r,c);
}
return 0;
}
数列分块入门 9
求区间最小众数。
其实这个题我 TLE 92pts改了块长过了,据说离散化后改块长可过,但是这个代码已经能过蒲公英那道题了,就懒得改了。
首先用 map
离散化,首先预处理一下每个块内的众数,同时我们要把每个数出现的位置 push
到以这个数为下标的 vector
里。这样我们查询某一个区间内这个数的个数,只需要这样即可:
inline int getcnt(int l,int r,int x){
return upper_bound(b[x].begin(),b[x].end(),r)-lower_bound(b[x].begin(),b[x].end(),l);
}
这样散点我们就能暴力统计了。大块直接取 \(\max\)。
UPD:经过神犇 RubyonlyK 的测试,最佳块长是 30。
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+10;
int n,m,S;
int a[maxn];
char buf[1<<20],*p1,*p2;
#define gc() (p1==p2?(p2=buf+fread(p1=buf,1,1<<20,stdin),p1==p2?EOF:*p1++):*p1++)
inline int read(){
int x=0;bool fopt=1;char ch=gc();
for(;!isdigit(ch);ch=gc())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=gc())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
int id;
int belong[maxn],L[maxn],R[maxn],v[maxn],cnt[maxn];
unordered_map<int,int> mp;
vector<int> b[maxn];
inline void Init(){
for(int i=1;i<=n;i++){
belong[i]=(i-1)/S+1;
if(!L[belong[i]])L[belong[i]]=i;
R[belong[i]]=i;
if(mp[a[i]]==0){
mp[a[i]]=++id;v[id]=a[i];//同时还要记录原来该数是什么
}
a[i]=mp[a[i]];
b[a[i]].push_back(i);//push下标进去
}
}
int f[5000+10][5000+10];//预处理块最小众数
void Prework(int x){
memset(cnt,0,sizeof(cnt));
int Max=0,ans=0;
for(int i=L[x];i<=n;i++){
int temp=++cnt[a[i]];
if(temp>Max||temp==Max&&v[a[i]]<v[ans]){
Max=temp;
ans=a[i];
}
f[x][belong[i]]=ans;
}
}
inline int getcnt(int l,int r,int x){
return upper_bound(b[x].begin(),b[x].end(),r)-lower_bound(b[x].begin(),b[x].end(),l);
}
inline int Solve(int l,int r){
int ans=f[belong[l]+1][belong[r]-1],Max=getcnt(l,r,ans);
for(int i=l;i<=min(R[belong[l]],r);i++){
int temp=getcnt(l,r,a[i]);
if(temp>Max||temp==Max&&v[a[i]]<v[ans]){
Max=temp;ans=a[i];
}
}
if(belong[l]!=belong[r]){
for(int i=L[belong[r]];i<=r;i++){
int temp=getcnt(l,r,a[i]);
if(temp>Max||temp==Max&&v[a[i]]<v[ans]){
Max=temp;ans=a[i];
}
}
}
return ans;
}
signed main(){
n=read();S=30;
for(int i=1;i<=n;i++)
a[i]=read();
Init();
for(int i=1;i<=belong[n];i++)
Prework(i);
int last=0;
for(int i=1;i<=n;i++){
int l=read(),r=read();
if(l>r)swap(l,r);
last=v[Solve(l,r)];
printf("%lld\n",last);
}
return 0;
}
总结:
暴力就完了。