LOJ分块⑨题汇总

从零开始的分块学习系列(感谢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
",a[t3]+laz[blo[t3]]);
40     }
41     return 0;
42 } 
View Code

序列分块之④

区间加法+区间求和

用来熟悉分块的题目

和上一题差不多,还是整块打标记+零散区间暴力,只需再对每个块维护一个总和即可

 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
",ans);
54         }
55     }
56     return 0;
57 } 
View Code

序列分块之⑦

区间加法+区间乘法+单点查询

仍然是熟悉分块的题目(因为这些能用线段树解决的问题一般我们不写分块=。=),不过新出现了一种做分块时的常见操作

和线段树的打标记差不多,打两个标记。然后对于整块的修改:加法只修改加法标记,乘法把加法乘法标记一起修改了。然后就发现对于零散区间不好处理标记,但是每次的零散区间总长不会超过$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
",(a[t3]*laz[blo[t3]][1]%mod+laz[blo[t3]][0])%mod);
60     }
61     return 0;
62 } 
View Code

序列分块之⑧

区间查询等于某个值的元素个数,然后再把整个区间赋成这个元素

真正开始进入分块的核心思想的题目

好了,大部分的数据结构应该是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
",ans);
57     }
58     return 0;
59 }
View Code

序列分块之⑤

区间开平方+区间求和

感觉是对上一题的一个巩固

其实很多人应该做过这个题的线段树版本;用分块做这个题和上一个题有点类似(其实好像比上一个题简单,但是上一个题才真正地体现出分块的核心思想)。同样是(全部)暴力修改+在块上打标记,在整块都变成$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
",ans);
64         }
65     }
66     return 0;
67 }
View Code

序列分块之⑥

单点插入+单点查询

也是一道体现着分块的思想的题(虽然平衡树也可以做),这里我们会稍微转到序列分块⑦那里提到的一个做法,不过也沿用了一些上两题的思想

我们分块后把每个块装进一个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
",v[blo][pos]);
54         }
55     }
56     return 0;
57 } 
View Code

 
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
",ans);
66         }
67     }
68     return 0;
69 }
View Code

序列分块之③

区间加法+区间查询某个数的前驱

和上一题挺像的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
",ans);
77         }
78     }
79     return 0;
80 } 
View Code

序列分块之⑨

静态区间众数(听说陈立杰做到过$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
",uni[ans]);
83     }
84     return 0;
85 }
View Code
原文地址:https://www.cnblogs.com/ydnhaha/p/9954881.html