zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    单例模式的七种写法
    Android省电开发 浅析
    android省电开发之cpu降频
    什么是签名、为什么要给应用程序签名、如何给应用程序签名
    内存溢出和内存泄漏的区别、产生原因以及解决方案
    Android Studio 使用GitHub
    sharesdk 的使用
    当ViewPager嵌套在ScrollView/ListView里时,手势冲突如何处理?
    laravel安装笔记
    erlang的一些小技巧(不定期更新)
  • 原文地址:https://www.cnblogs.com/ydnhaha/p/9954881.html
Copyright © 2011-2022 走看看