从零开始的分块学习系列(感谢hzwer)
题目顺序是我建议的做题顺序
先说一句:分块的核心思想(其实本身分块就可以说是一种思想)是:均摊(或者说平衡/权衡?)复杂度,同时这种思想本身不只局限于序列分块(前一篇解题里有提到)
序列分块之①
区间加法+单点查询
分块入门题
知道分块的思想之后应该都会做,对整块打标记,对不超过块大小的零散区间暴力修改;查询的时候就是原数+所在块的标记
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=50005,Sq=230; 7 int a[N],blo[N],laz[Sq],pts[Sq][2]; 8 int n,m,t1,t2,t3,t4,cnt,sqr; 9 int main() 10 { 11 scanf("%d",&n),m=n; 12 pts[cnt=1][0]=1,sqr=sqrt(n)+10; 13 for(int i=1;i<=n;i++) 14 { 15 scanf("%d",&a[i]); 16 blo[i]=(i-1)/sqr+1; 17 if(i%sqr==0) 18 { 19 pts[cnt++][1]=i; 20 pts[cnt][0]=i+1; 21 } 22 } 23 pts[cnt][1]=n; 24 while(m--) 25 { 26 scanf("%d%d%d%d",&t1,&t2,&t3,&t4); 27 if(!t1) 28 { 29 if(blo[t2]!=blo[t3]) 30 { 31 for(int i=t2;i<=pts[blo[t2]][1];i++) a[i]+=t4; 32 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) laz[i]+=t4; 33 for(int i=pts[blo[t3]][0];i<=t3;i++) a[i]+=t4; 34 } 35 else 36 for(int i=t2;i<=t3;i++) a[i]+=t4; 37 } 38 else 39 printf("%d\n",a[t3]+laz[blo[t3]]); 40 } 41 return 0; 42 }
序列分块之④
区间加法+区间求和
用来熟悉分块的题目
和上一题差不多,还是整块打标记+零散区间暴力,只需再对每个块维护一个总和即可
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=50005,Sq=230; 7 long long laz[Sq],pts[Sq][2]; 8 long long a[N],blo[N],sum[Sq]; 9 long long n,m,t1,t2,t3,t4,cnt,sqr; 10 int main() 11 { 12 scanf("%lld",&n),m=n; 13 sqr=sqrt(n)+10,pts[cnt=1][0]=1; 14 for(int i=1;i<=n;i++) 15 scanf("%lld",&a[i]); 16 for(int i=1;i<=n;i++) 17 { 18 blo[i]=(i-1)/sqr+1; 19 sum[blo[i]]+=a[i]; 20 if(i%sqr==0) 21 { 22 pts[cnt++][1]=i; 23 pts[cnt][0]=i+1; 24 } 25 } 26 pts[cnt][1]=n; 27 while(m--) 28 { 29 scanf("%lld%lld%lld%lld",&t1,&t2,&t3,&t4); 30 if(!t1) 31 { 32 if(blo[t2]!=blo[t3]) 33 { 34 for(int i=t2;i<=pts[blo[t2]][1];i++) a[i]+=t4,sum[blo[i]]+=t4; 35 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) laz[i]+=t4; 36 for(int i=pts[blo[t3]][0];i<=t3;i++) a[i]+=t4,sum[blo[i]]+=t4; 37 } 38 else 39 for(int i=t2;i<=t3;i++) a[i]+=t4,sum[blo[i]]+=t4; 40 } 41 else 42 { 43 long long ans=0; 44 if(blo[t2]!=blo[t3]) 45 { 46 for(int i=t2;i<=pts[blo[t2]][1];i++) ans=(ans+a[i]+laz[blo[i]])%(t4+1); 47 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) 48 ans=(ans+sum[i]+laz[i]*(pts[i][1]-pts[i][0]+1)%(t4+1))%(t4+1); 49 for(int i=pts[blo[t3]][0];i<=t3;i++) ans=(ans+a[i]+laz[blo[i]])%(t4+1); 50 } 51 else 52 for(int i=t2;i<=t3;i++) ans=(ans+a[i]+laz[blo[i]])%(t4+1); 53 printf("%lld\n",ans); 54 } 55 } 56 return 0; 57 }
序列分块之⑦
区间加法+区间乘法+单点查询
仍然是熟悉分块的题目(因为这些能用线段树解决的问题一般我们不写分块=。=),不过新出现了一种做分块时的常见操作
和线段树的打标记差不多,打两个标记。然后对于整块的修改:加法只修改加法标记,乘法把加法乘法标记一起修改了。然后就发现对于零散区间不好处理标记,但是每次的零散区间总长不会超过$2*sqrt(n)$,所以直接把零散区间暴力重构然后清空标记,仍然可以保证复杂度。查询就挺简单的,不说了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=100005,Sq=320; 6 const long long mod=10007; 7 long long n,m,t1,t2,t3,t4,cnt; 8 long long a[N],blo[N],laz[Sq][2],pts[Sq][2]; 9 void rebuild(int b,int l,int r,int v,int t) 10 { 11 for(int i=pts[b][0];i<=pts[b][1];i++) 12 a[i]=(a[i]*laz[b][1]%mod+laz[b][0])%mod; 13 laz[b][0]=0,laz[b][1]=1; 14 if(!t) for(int i=l;i<=r;i++) a[i]=(a[i]+v)%mod; 15 else for(int i=l;i<=r;i++) a[i]=a[i]*v%mod; 16 } 17 int main() 18 { 19 scanf("%lld",&n),m=n; 20 sqr=sqrt(n)+10,pts[cnt=1][0]=1; 21 for(int i=1;i<=n;i++) 22 scanf("%lld",&a[i]); 23 for(int i=1;i<=n;i++) 24 { 25 blo[i]=(i-1)/sqr+1; 26 if(i%sqr==0) 27 { 28 pts[cnt++][1]=i; 29 pts[cnt][0]=i+1; 30 } 31 } 32 pts[cnt][1]=n; 33 for(int i=1;i<=cnt;i++) laz[i][1]=1; 34 while(m--) 35 { 36 scanf("%lld%lld%lld%lld",&t1,&t2,&t3,&t4); 37 if(!t1) 38 { 39 if(blo[t2]!=blo[t3]) 40 { 41 rebuild(blo[t2],t2,pts[blo[t2]][1],t4,0); 42 rebuild(blo[t3],pts[blo[t3]][0],t3,t4,0); 43 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) laz[i][0]=(laz[i][0]+t4)%mod; 44 } 45 else rebuild(blo[t2],t2,t3,t4,0); 46 } 47 else if(t1==1) 48 { 49 if(blo[t2]!=blo[t3]) 50 { 51 rebuild(blo[t2],t2,pts[blo[t2]][1],t4,1); 52 rebuild(blo[t3],pts[blo[t3]][0],t3,t4,1); 53 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) 54 laz[i][0]=laz[i][0]*t4%mod,laz[i][1]=laz[i][1]*t4%mod; 55 } 56 else rebuild(blo[t2],t2,t3,t4,1); 57 } 58 else 59 printf("%lld\n",(a[t3]*laz[blo[t3]][1]%mod+laz[blo[t3]][0])%mod); 60 } 61 return 0; 62 }
序列分块之⑧
区间查询等于某个值的元素个数,然后再把整个区间赋成这个元素
真正开始进入分块的核心思想的题目
好了,大部分的数据结构应该是GG了(也许有什么神仙数据结构能做),怎么破?
想想开头的那句话
我们可以发现,因为每次是把整个区间改成一个数,所以:
对于被整块覆盖的区间,我们好像只能暴力修改?莫慌,分析一下:如果它中间有很多个数,我们暴力修改完之后可以把整块打上标记,下次再查到这个块就直接$O(1)$统计了;如果它已经有标记了,那就更好了,直接$O(1)$统计+改标记即可
然后发现零散区间好像很麻烦,按照以往的套路我们还应该暴力修改它们,然而改完了之后区间的标记就没了,情感上好像复杂度不能接受
那我们理性分析一下
暴力修改一次最多只会破坏两个块的标记,这样至少经过$O(sqrt(n))$这个级别的修改后整个序列才又回到了初始的没标记的情况,这时候我们才会有$O(n)$的序列暴力修改,所以其实复杂度是$O(n$ $sqrt(n))$的,可以通过
这就是我开头所谓的“权衡/平衡复杂度”了
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=100005,Sq=320; 7 int n,m,t1,t2,t3,t4,sqr,cnt; 8 int a[N],blo[N],laz[Sq][2],pts[Sq][2]; 9 int rebuild(int b,int l,int r,int v) 10 { 11 int ret=0; 12 if(laz[b][0]) 13 { 14 laz[b][0]=(laz[b][1]==v); 15 for(int i=pts[b][0];i<=pts[b][1];i++) 16 a[i]=laz[b][1]; 17 } 18 for(int i=l;i<=r;i++) 19 ret+=(a[i]==v),a[i]=v; 20 return ret; 21 } 22 int main () 23 { 24 scanf("%d",&n),m=n; 25 sqr=sqrt(n)+10,pts[cnt=1][0]=1; 26 for(int i=1;i<=n;i++) 27 { 28 scanf("%d",&a[i]); 29 blo[i]=(i-1)/sqr+1; 30 if(i%sqr==0) 31 { 32 pts[cnt++][1]=i; 33 pts[cnt][0]=i+1; 34 } 35 } 36 pts[cnt][1]=n; 37 while(m--) 38 { 39 int ans=0; 40 scanf("%d%d%d",&t1,&t2,&t3); 41 if(blo[t1]!=blo[t2]) 42 { 43 ans+=rebuild(blo[t1],t1,pts[blo[t1]][1],t3); 44 ans+=rebuild(blo[t2],pts[blo[t2]][0],t2,t3); 45 for(int i=blo[t1]+1;i<=blo[t2]-1;i++) 46 if(laz[i][0]) 47 ans+=(pts[i][1]-pts[i][0]+1)*(laz[i][1]==t3),laz[i][1]=t3; 48 else 49 { 50 laz[i][0]=true,laz[i][1]=t3; 51 for(int j=pts[i][0];j<=pts[i][1];j++) 52 ans+=(a[j]==t3),a[j]=t3; 53 } 54 } 55 else ans+=rebuild(blo[t1],t1,t2,t3); 56 printf("%d\n",ans); 57 } 58 return 0; 59 }
序列分块之⑤
区间开平方+区间求和
感觉是对上一题的一个巩固
其实很多人应该做过这个题的线段树版本;用分块做这个题和上一个题有点类似(其实好像比上一个题简单,但是上一个题才真正地体现出分块的核心思想)。同样是(全部)暴力修改+在块上打标记,在整块都变成$0$或$1$之后这个块再开平方就不变了,所以跳过这种块即可。而一个数开平方是下降地非常快的,开平方的次数基本可以看做一个常数,这样总体也是$O(n$ $sqrt(n))$的
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=50005,Sq=230; 7 int n,m,t1,t2,t3,t4,cnt,sqr; 8 int a[N],blo[N],sum[Sq]; 9 int laz[Sq],pts[Sq][2]; 10 int main () 11 { 12 scanf("%d",&n),m=n; 13 sqr=sqrt(n)+10,pts[cnt=1][0]=1; 14 for(int i=1;i<=n;i++) 15 { 16 scanf("%d",&a[i]); 17 blo[i]=(i-1)/sqr+1; 18 sum[blo[i]]+=a[i]; 19 if(i%sqr==0) 20 { 21 pts[cnt++][1]=i; 22 pts[cnt][0]=i+1; 23 } 24 } 25 pts[cnt][1]=n; 26 while(m--) 27 { 28 scanf("%d%d%d%d",&t1,&t2,&t3,&t4); 29 if(!t1) 30 { 31 if(blo[t2]!=blo[t3]) 32 { 33 for(int i=t2;i<=pts[blo[t2]][1];i++) 34 sum[blo[i]]-=a[i],a[i]=(int)sqrt(a[i]),sum[blo[i]]+=a[i]; 35 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) 36 if(!laz[i]) 37 { 38 int lazy=true; 39 for(int j=pts[i][0];j<=pts[i][1];j++) 40 { 41 sum[i]-=a[j],a[j]=(int)sqrt(a[j]),sum[i]+=a[j]; 42 if(a[j]&&a[j]!=1) lazy=false; 43 } 44 if(lazy) laz[i]=true; 45 } 46 for(int i=pts[blo[t3]][0];i<=t3;i++) 47 sum[blo[i]]-=a[i],a[i]=(int)sqrt(a[i]),sum[blo[i]]+=a[i]; 48 } 49 else 50 for(int i=t2;i<=t3;i++) 51 sum[blo[i]]-=a[i],a[i]=(int)sqrt(a[i]),sum[blo[i]]+=a[i]; 52 } 53 else 54 { 55 int ans=0; 56 if(blo[t2]!=blo[t3]) 57 { 58 for(int i=t2;i<=pts[blo[t2]][1];i++) ans+=a[i]; 59 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) ans+=sum[i]; 60 for(int i=pts[blo[t3]][0];i<=t3;i++) ans+=a[i]; 61 } 62 else for(int i=t2;i<=t3;i++) ans+=a[i]; 63 printf("%d\n",ans); 64 } 65 } 66 return 0; 67 }
序列分块之⑥
单点插入+单点查询
也是一道体现着分块的思想的题(虽然平衡树也可以做),这里我们会稍微转到序列分块⑦那里提到的一个做法,不过也沿用了一些上两题的思想
我们分块后把每个块装进一个vector里,然后直接用vector在对应位置插入。发现问题是当很多次插入之后一个块的vector可能会非常长,导致插入的复杂度变得很大;所以我们在一个vector超过某个长度$limit$(理论上是$2*sqrt(n)$,不过一会会说一些其他的东西)之后直接把所有vector清空,重构整个序列!类似序列分块⑧地分析:这样每最坏每$limit$次插入后会来一次$O(n)$的重构,所以复杂度是$O(n(\frac{n}{limit}+limit))$的。
看起来$limit$只要加上往常的块大小设置为$2*sqrt(n)$即可。不过实际情况中因为vector的常数实在太大了,导致暴力重构效率较低,所以其实把$limit$设得大一些反而效率更高=。=(我最后设的$10*sqrt(n)$)
1 #include<cmath> 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int N=200005,Sq=460; 8 int n,m,t1,t2,t3,t4,sqr,cnt,a[N]; 9 vector<int> v[Sq]; 10 pair<int,int> query(int pos) 11 { 12 int noww=1; 13 while(pos>=(int)v[noww].size()) 14 pos-=(int)v[noww++].size(); 15 return make_pair(pos,noww); 16 } 17 void rebuild() 18 { 19 int p=0; 20 for(int i=1;i<=cnt;i++) 21 { 22 for(int j=0;j<(int)v[i].size();j++) 23 a[++p]=v[i][j]; 24 v[i].clear(); 25 } 26 sqr=sqrt(p)+10,cnt=(p-1)/sqr+1; 27 for(int i=1;i<=p;i++) 28 v[(i-1)/sqr+1].push_back(a[i]); 29 } 30 int main () 31 { 32 scanf("%d",&n),m=n; 33 sqr=sqrt(n)+10,cnt=(n-1)/sqr+1; 34 for(int i=1;i<=n;i++) 35 { 36 scanf("%d",&t1); 37 v[(i-1)/sqr+1].push_back(t1); 38 } 39 while(m--) 40 { 41 scanf("%d%d%d%d",&t1,&t2,&t3,&t4); 42 if(!t1) 43 { 44 pair<int,int> q=query(t2-1); 45 int pos=q.first,blo=q.second; 46 v[blo].insert(v[blo].begin()+pos,t3); 47 if((int)v[blo].size()>10*sqr) rebuild(); 48 } 49 else 50 { 51 pair<int,int> q=query(t3-1); 52 int pos=q.first,blo=q.second; 53 printf("%d\n",v[blo][pos]); 54 } 55 } 56 return 0; 57 }
Update on 2018.11.14:蒟蒻博主发现序列分块②③⑨的块大小其实不太对,应该是$sqrt(\frac{n}{\log n})$,然而我的是$sqrt(\frac{n}{\ln n})$,不过最近LOJ老出锅就懒得改了
序列分块之②
区间加法+查询区间小于某个数的元素之和
做到这里应该有点感觉了吧quq
还是个套路的做法,把每个块装进一个vector里,然后排好序。每次修改对于零散区间暴力清空然后重构vector,对于整块的打上标记
注意这时候有了排序的操作,我们分块的大小应该跟着变成$O(sqrt(n/log$ $n))$,复杂度$O(nlog$ $n$ $sqrt(nlog$ $n))$
1 #include<cmath> 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int N=50005,Sq=230; 8 int n,m,t1,t2,t3,t4,sqr,cnt; 9 int a[N],blo[N],laz[Sq],pts[Sq][2]; 10 vector<int> v[Sq]; 11 void rebuild(int b,int l,int r,int t) 12 { 13 for(int i=l;i<=r;i++) a[i]+=t; 14 v[b].clear(); 15 for(int i=pts[b][0];i<=pts[b][1];i++) 16 v[b].push_back(a[i]); 17 sort(v[b].begin(),v[b].end()); 18 } 19 int main() 20 { 21 scanf("%d",&n),m=n; 22 sqr=sqrt(n)+10,pts[cnt=1][0]=1; 23 for(int i=1;i<=n;i++) 24 { 25 scanf("%d",&a[i]); 26 blo[i]=(i-1)/sqr+1; 27 v[blo[i]].push_back(a[i]); 28 if(i%sqr==0) 29 { 30 pts[cnt++][1]=i; 31 pts[cnt][0]=i+1; 32 } 33 } 34 pts[cnt][1]=n; 35 for(int i=1;i<=cnt;i++) 36 sort(v[i].begin(),v[i].end()); 37 while(m--) 38 { 39 scanf("%d%d%d%d",&t1,&t2,&t3,&t4); 40 if(!t1) 41 { 42 if(blo[t2]!=blo[t3]) 43 { 44 rebuild(blo[t2],t2,pts[blo[t2]][1],t4); 45 rebuild(blo[t3],pts[blo[t3]][0],t3,t4); 46 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) laz[i]+=t4; 47 } 48 else rebuild(blo[t2],t2,t3,t4); 49 } 50 else 51 { 52 int ans=0; t4*=t4; 53 if(blo[t2]!=blo[t3]) 54 { 55 for(int i=t2;i<=pts[blo[t2]][1];i++) 56 if(a[i]+laz[blo[i]]<t4) ans++; 57 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) 58 ans+=lower_bound(v[i].begin(),v[i].end(),t4-laz[i])-v[i].begin(); 59 for(int i=pts[blo[t3]][0];i<=t3;i++) 60 if(a[i]+laz[blo[i]]<t4) ans++; 61 } 62 else 63 for(int i=t2;i<=t3;i++) 64 if(a[i]+laz[blo[i]]<t4) ans++; 65 printf("%d\n",ans); 66 } 67 } 68 return 0; 69 }
序列分块之③
区间加法+区间查询某个数的前驱
和上一题挺像的quq
修改时暴力重构零散区间+整块打标记,查询时仍然暴力重构零散区间+块内二分,然后还是注意块的大小
1 #include<cmath> 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int N=100005,Sq=320; 8 int a[N],blo[N],tmp[N]; 9 int laz[Sq],pts[Sq][2]; 10 int n,m,t1,t2,t3,t4,cnt,sqr; 11 vector<int> v[Sq]; 12 void rebuild(int b,int l,int r,int t) 13 { 14 for(int i=l;i<=r;i++) a[i]+=t; 15 v[b].clear(); 16 for(int i=pts[b][0];i<=pts[b][1];i++) 17 v[b].push_back(a[i]); 18 sort(v[b].begin(),v[b].end()); 19 } 20 int check(int b,int l,int r,int t) 21 { 22 int p=0; 23 for(int i=l;i<=r;i++) 24 tmp[p++]=a[i]+laz[b]; 25 sort(tmp,tmp+p); 26 int pos=lower_bound(tmp,tmp+p,t)-tmp-1; 27 return (~pos)?tmp[pos]:-1; 28 } 29 int main() 30 { 31 scanf("%d",&n),m=n; 32 sqr=sqrt(n)+10,pts[cnt=1][0]=1; 33 for(int i=1;i<=n;i++) 34 { 35 scanf("%d",&a[i]); 36 blo[i]=(i-1)/sqr+1; 37 v[blo[i]].push_back(a[i]); 38 if(i%sqr==0) 39 { 40 pts[cnt++][1]=i; 41 pts[cnt][0]=i+1; 42 } 43 } 44 pts[cnt][1]=n; 45 for(int i=1;i<=cnt;i++) 46 sort(v[i].begin(),v[i].end()); 47 while(m--) 48 { 49 scanf("%d%d%d%d",&t1,&t2,&t3,&t4); 50 if(!t1) 51 { 52 if(blo[t2]!=blo[t3]) 53 { 54 rebuild(blo[t2],t2,pts[blo[t2]][1],t4); 55 rebuild(blo[t3],pts[blo[t3]][0],t3,t4); 56 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) laz[i]+=t4; 57 } 58 else 59 rebuild(blo[t2],t2,t3,t4); 60 } 61 else 62 { 63 int ans=-1; 64 if(blo[t2]!=blo[t3]) 65 { 66 ans=max(ans,check(blo[t2],t2,pts[blo[t2]][1],t4)); 67 ans=max(ans,check(blo[t3],pts[blo[t3]][0],t3,t4)); 68 for(int i=blo[t2]+1;i<=blo[t3]-1;i++) 69 { 70 int pos=lower_bound(v[i].begin(),v[i].end(),t4-laz[i])-v[i].begin()-1; 71 if(~pos) ans=max(ans,v[i][pos]+laz[i]); 72 } 73 } 74 else 75 ans=max(ans,check(blo[t2],t2,t3,t4)); 76 printf("%d\n",ans); 77 } 78 } 79 return 0; 80 }
序列分块之⑨
静态区间众数(听说陈立杰做到过$O(n^{\frac{5}{3}})$的带修区间众数 orz)
(其实我们平时用分块写的大多是这样的题)
注意到答案只可能是整块的众数或者出现在零散区间的数
先离散化,预处理$mde[i][j]$表示第$i$块与第$j$块间的众数(以每个块为起点开个桶扫一遍,复杂度$O(n$ $sqrt(n))$),然后把每种数的位置塞进一个对应的vector里,这样对于零散区间就可以直接二分了,复杂度$O(nlog$ $n$ $sqrt(nlog$ $n))$
1 #include<cmath> 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int N=100005,Sq=2000,inf=2147483647; 8 int a[N],uni[N],blo[N],tmp[N],pts[Sq][2],mde[Sq][Sq]; 9 int n,m,t1,t2,cnt,sqr,mem,ans; 10 vector<int> pos[N]; 11 bool better(int c1,int v1,int c2,int v2) 12 { 13 return (c1>c2||(c1==c2&&v1<v2)); 14 } 15 void prework() 16 { 17 sort(uni+1,uni+1+n); 18 int len=unique(uni+1,uni+1+n)-uni-1; 19 for(int i=1;i<=n;i++) 20 { 21 a[i]=lower_bound(uni+1,uni+1+len,a[i])-uni; 22 pos[a[i]].push_back(i); 23 } 24 for(int i=1;i<=cnt;i++) 25 { 26 int ncnt=0,nnum=inf; 27 memset(tmp,0,sizeof tmp); 28 for(int j=pts[i][0];j<=n;j++) 29 { 30 tmp[a[j]]++; 31 if(better(tmp[a[j]],a[j],ncnt,nnum)) 32 ncnt=tmp[a[j]],nnum=a[j]; 33 mde[i][blo[j]]=nnum; 34 } 35 } 36 } 37 int ask(int v) 38 { 39 vector<int>::iterator rr=upper_bound(pos[v].begin(),pos[v].end(),t2); 40 vector<int>::iterator ll=lower_bound(pos[v].begin(),pos[v].end(),t1); 41 return rr-ll; 42 } 43 void force(int l,int r) 44 { 45 for(int i=l;i<=r;i++) 46 { 47 int qry=ask(a[i]); 48 if(better(qry,a[i],mem,ans)) 49 mem=qry,ans=a[i]; 50 } 51 return ; 52 } 53 int main () 54 { 55 scanf("%d",&n),m=n; 56 sqr=sqrt((double)n/log(n)),pts[cnt=1][0]=1; 57 for(int i=1;i<=n;i++) 58 { 59 scanf("%d",&a[i]),uni[i]=a[i]; 60 blo[i]=(i-1)/sqr+1; 61 if(i%sqr==0) 62 { 63 pts[cnt++][1]=i; 64 pts[cnt][0]=i+1; 65 } 66 } 67 pts[cnt][1]=n; prework(); 68 while(m--) 69 { 70 mem=0,ans=inf; 71 scanf("%d%d",&t1,&t2); 72 if(blo[t1]!=blo[t2]) 73 { 74 if(blo[t1]+1<=blo[t2]-1) 75 { 76 mem=ask(mde[blo[t1]+1][blo[t2]-1]); 77 ans=mde[blo[t1]+1][blo[t2]-1]; 78 } 79 force(t1,pts[blo[t1]][1]),force(pts[blo[t2]][0],t2); 80 } 81 else force(t1,t2); 82 printf("%d\n",uni[ans]); 83 } 84 return 0; 85 }
来源:https://www.cnblogs.com/ydnhaha/p/9954881.html