从零开始的分块学习系列(感谢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
