zoukankan      html  css  js  c++  java
  • 数据结构 — 分块 (学习历程) 连载中......

     

    1.  区间加法  单点查值 
    • 把每m个元素分为一块,共有n/m块,每次区间加的操作会涉及O(n/m)个整块,以及区间两侧两个不完整的块中至多2m个元素。
    • 我们给每个块设置一个加法标记 atage(记录这个块中元素一起加了多少),每次操作对每个整块直接O(1)标记,而不完整的块由于元素比较少,暴力修改元素的值。每次询问时返回元素的值加上其所在块的加法标记。
    • 这样每次操作的复杂度是O(n/m)+O(m),根据均值不等式,当m取√n时总复杂度最低。
     1 #include<cstdio>
     2 #include<cmath>
     3 #include<algorithm> 
     4 using namespace std;
     5 int bl[50010];    //bl[i]代表第 i个数位于哪个块
     6 int atag[50010];    //存储每个块内 操作加值的和
     7 int block,n,a[50010];
     8 int read(){
     9     int x=0,f=1;
    10     char c=getchar();
    11     while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}    //注意大括号不能省 
    12     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    13     return x*f;
    14 }
    15 void add(int l,int r,int c){
    16     for(int i=l;i<=min(bl[l]*block,r);i++) a[i]+=c;    //区间覆盖的左半块
    17     if(bl[l]!=bl[r]){        //不在同一个块时 
    18         for(int i=(bl[r]-1)*block+1;i<=r;i++) a[i]+=c;    //区间覆盖的右半块 
    19     }
    20     for(int i=bl[l]+1;i<=bl[r]-1;i++) atag[i]+=c;    //中间的整块整块处理 
    21 }
    22 int main()
    23 {
    24     n=read();
    25     block=sqrt(n);        //每个块的大小
    26     for(int i=1;i<=n;i++) a[i]=read();
    27     for(int i=1;i<=n;i++) bl[i]=(i-1)/block+1;    //分块
    28     //比如 block=2 那么1 2是属于第一个块的,3 4属于第二个块 这样处理刚好能满足需求
    29     for(int i=1;i<=n;i++){
    30         int opt=read(),l=read(),r=read(),c=read();
    31         if(opt==0) add(l,r,c);
    32         else printf("%d
    ",a[r]+atag[bl[r]]);
    33     }
    34     return 0;
    35 }
    View Code

      2. 区间加法  询问区间内小于x的元素个数  (vector  块内排序  lower_bound)

     1 #include<cstdio>
     2 #include<vector>
     3 #include<cmath>
     4 #include<algorithm>
     5 #define N 50010
     6 using namespace std;
     7 int a[N],bl[N],atage[N],n,block;
     8 vector <int > v[N];    //动态数组 
     9 int read(){
    10     int x=0,f=1;
    11     char c=getchar();
    12     while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    13     while(c>='0'&&c<='9')  x=x*10+c-'0',c=getchar();
    14     return x*f;
    15 }
    16 void  reset(int x){
    17     v[x].clear();    //清除x块内元素 
    18     for(int i=(x-1)*block+1;i<=x*block;i++) v[x].push_back(a[i]);
    19     //(x-1)*block+1 表示第 x-1块内的末元素+1,也就是第 x块的第一个元素
    20     // x*block 表示 第 x块的末元素 
    21     sort(v[x].begin(),v[x].end());
    22 }
    23 void add(int l,int r,int c){
    24     for(int i=l;i<=min(bl[l]*block,r);i++) a[i]+=c;    //最左边的(半)块 
    25     reset(bl[l]);    //a[i]的值改变  容器里的值也要更新 
    26     if(bl[l]!=bl[r]){
    27         for(int i=(bl[r]-1)*block+1;i<=r;i++) a[i]+=c;    //最右边的(半)块
    28         reset(bl[r]);
    29     }
    30     for(int i=bl[l]+1;i<=bl[r]-1;i++) atage[i]+=c;    //中间的整块整块处理 
    31 }
    32 void ask(int l,int r,int c){
    33     int sum=0;
    34     for(int i=l;i<=min(bl[l]*block,r);i++) if(a[i]+atage[bl[i]]<c) sum++;
    35     if(bl[l]!=bl[r]){
    36         for(int i=(bl[r]-1)*block+1;i<=r;i++) if(a[i]+atage[bl[i]]<c) sum++;
    37     }
    38     for(int i=bl[l]+1;i<=bl[r]-1;i++){
    39         int x=lower_bound(v[i].begin(),v[i].end(),c-atage[i])-v[i].begin(); 
    40         sum+=x;
    41     }
    42     printf("%d
    ",sum);
    43 }
    44 int main()
    45 {
    46     n=read();
    47     block=sqrt(n);
    48     for(int i=1;i<=n;i++) a[i]=read();
    49     for(int i=1;i<=n;i++){
    50         bl[i]=(i-1)/block+1;
    51         v[bl[i]].push_back(a[i]);
    52     }
    53     for(int i=1;i<=bl[n];i++) sort(v[i].begin(),v[i].end());     //块内排序 
    54     for(int i=1;i<=n;i++){
    55         int opt=read(),l=read(),r=read(),c=read();
    56         if(opt==0) add(l,r,c);
    57         else ask(l,r,c*c);
    58     }
    59     return 0;
    60 }
    View Code

      3.  区间加法  询问区间内小于某个值x的前驱(比其小的最大元素   (set  lower_bound)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<set>
     4 #define N 100010
     5 using namespace std;
     6 int read(){
     7     int x=0,f=1; char c=getchar();
     8     while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
     9     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    10     return x*f;
    11 }
    12 int n,blo;
    13 int v[N],bl[N],atag[N];
    14 set <int > st[110];
    15 void add(int l,int r,int c){
    16     for(int i=l;i<=min(r,bl[l]*blo);i++){
    17         st[bl[l]].erase(v[i]);
    18         v[i]+=c;
    19         st[bl[l]].insert(v[i]);
    20     }
    21     if(bl[l]!=bl[r]){
    22         for(int i=(bl[r]-1)*blo+1;i<=r;i++){
    23             st[bl[r]].erase(v[i]);
    24             v[i]+=c;
    25             st[bl[r]].insert(v[i]);
    26         }
    27     }
    28     for(int i=bl[l]+1;i<=bl[r]-1;i++)
    29         atag[i]+=c;
    30 }
    31 int ask(int l,int r,int c){
    32     int ans=-1;
    33     for(int i=l;i<=min(r,bl[l]*blo);i++){
    34         int val=v[i]+atag[bl[l]];
    35         if(val<c) ans=max(val,ans);
    36     }
    37     if(bl[l]!=bl[r]){
    38         for(int i=(bl[r]-1)*blo+1;i<=r;i++){
    39             int val=v[i]+atag[bl[r]];
    40             if(val<c) ans=max(val,ans);
    41         }
    42     }
    43     for(int i=bl[l]+1;i<=bl[r]-1;i++){
    44         int x=c-atag[i];
    45         set <int > ::iterator it=st[i].lower_bound(x);
    46         if(it==st[i].begin()) continue;
    47         --it;
    48         ans=max(ans,*it+atag[i]);
    49     }
    50     return ans;
    51 }
    52 int main()
    53 {
    54     n=read(),blo=1000;
    55     for(int i=1;i<=n;i++) v[i]=read();
    56     for(int i=1;i<=n;i++){
    57         bl[i]=(i-1)/blo+1;
    58         st[bl[i]].insert(v[i]);
    59     }
    60     for(int i=1;i<=n;i++){
    61         int opt=read(),a=read(),b=read(),c=read();
    62         if(opt==0) add(a,b,c);
    63         else printf("%d
    ",ask(a,b,c));
    64     } 
    65     return 0;
    66 }
    View Code

      4.  区间加法  区间求和 

     1 #include<cstdio>
     2 #include<cmath>
     3 #include<algorithm>
     4 #define N 50010
     5 #define ll long long        //真的不开 long long 见祖宗 T^T 
     6 using namespace std;
     7 ll read(){
     8     ll x=0,f=1; char c=getchar();
     9     while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    10     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    11     return x*f;
    12 }
    13 int n,blo,bl[N];
    14 ll a[N],sum[N],atag[N];        // 不能开小 
    15 void add(int l,int r,int c){
    16     for(int i=l;i<=min(bl[l]*blo,r);i++)
    17         a[i]+=c,sum[bl[l]]+=c;
    18     if(bl[l]!=bl[r]){
    19         for(int i=(bl[r]-1)*blo+1;i<=r;i++)
    20             a[i]+=c,sum[bl[r]]+=c;
    21     }
    22     for(int i=bl[l]+1;i<=bl[r]-1;i++)
    23         atag[i]+=c;
    24 }
    25 ll ask(int l,int r){
    26     ll ans=0;
    27     for(int i=l;i<=min(bl[l]*blo,r);i++)
    28         ans+=a[i]+atag[bl[l]];
    29     if(bl[l]!=bl[r]){
    30         for(int i=(bl[r]-1)*blo+1;i<=r;i++)
    31             ans+=a[i]+atag[bl[r]];
    32     }
    33     for(int i=bl[l]+1;i<=bl[r]-1;i++)
    34         ans+=sum[i]+atag[i]*blo;
    35     return ans;
    36 }
    37 int main(){
    38     n=read(),blo=sqrt(n);
    39     for(int i=1;i<=n;i++) a[i]=read();
    40     for(int i=1;i<=n;i++){
    41         bl[i]=(i-1)/blo+1;
    42         sum[bl[i]]+=a[i]; 
    43     }
    44     for(int i=1;i<=n;i++){
    45         int opt=read(),l=read(),r=read(),c=read();
    46         if(opt==0) add(l,r,c);
    47         else printf("%d
    ",ask(l,r)%(c+1));
    48     }
    49     return 0;
    50 }
    View Code

      5.  区间求和  区间开方 

     1 #include<cstdio>
     2 #include<cmath>
     3 #include<algorithm>
     4 #define N 50010
     5 #define ll long long
     6 using namespace std;
     7 ll read(){
     8     ll x=0,f=1; char c=getchar();
     9     while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    10     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    11     return x*f;
    12 }
    13 int n,blo;
    14 int  bl[N],a[N],sum[N],flag[N];
    15 void solve_sqrt(int x){
    16     if(flag[x]) return ;
    17     flag[x]=1;
    18     sum[x]=0;
    19     for(int i=(x-1)*blo+1;i<=x*blo;i++){
    20         a[i]=sqrt(a[i]),sum[x]+=a[i];
    21         if(a[i]>1) flag[x]=0;
    22     }
    23 }
    24 void add(int l,int r,int c){
    25     for(int i=l;i<=min(r,bl[l]*blo);i++){
    26         sum[bl[l]]-=a[i];
    27         a[i]=sqrt(a[i]);
    28         sum[bl[l]]+=a[i];
    29     }
    30     if(bl[l]!=bl[r]){
    31         for(int i=(bl[r]-1)*blo+1;i<=r;i++){
    32         sum[bl[r]]-=a[i];
    33         a[i]=sqrt(a[i]);
    34         sum[bl[r]]+=a[i];
    35         }
    36     }
    37     for(int i=bl[l]+1;i<=bl[r]-1;i++)
    38         solve_sqrt(i);
    39 }
    40 int ask(int l,int r){
    41     int ans=0;
    42     for(int i=l;i<=min(bl[l]*blo,r);i++)
    43         ans+=a[i];
    44     if(bl[l]!=bl[r]){
    45         for(int i=(bl[r]-1)*blo+1;i<=r;i++)
    46             ans+=a[i];
    47     }
    48     for(int i=bl[l]+1;i<=bl[r]-1;i++)
    49         ans+=sum[i];
    50     return ans;
    51 }
    52 int main()
    53 {
    54     n=read(),blo=sqrt(n);
    55     for(int i=1;i<=n;i++){
    56         a[i]=read();
    57         bl[i]=(i-1)/blo+1;
    58         sum[bl[i]]+=a[i];
    59     }
    60     for(int i=1;i<=n;i++){
    61         int opt=read(),l=read(),r=read(),c=read();
    62         if(opt==0) add(l,r,c);
    63         else printf("%d
    ",ask(l,r));
    64     }
    65     return 0;
    66 }
    View Code

      6.  单点插入,单点询问

     1 #include<cstdio>
     2 #include<cmath>
     3 #define re register
     4 #define in inline
     5 #define ll long long
     6 #define N 1010
     7 using namespace std;
     8 in int read(){
     9     int x=0,f=1; char c=getchar();
    10     while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    11     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    12     return x*f;
    13 }
    14 
    15 int n,m,blo;
    16 int new_point; //新增点数计数器 
    17 int a[N][N]; //a[i][j]记录第i块的第j个数
    18 int b[N][N];
    19 int len[N]; //len[i]记录第i块的个数 
    20 
    21 void rebuild(){
    22     int x=0,t=0;
    23     int k=m/blo;    //m 新总长
    24     if(m%blo!=0) k++;
    25     m+=blo; new_point=0;
    26     blo=sqrt(m);
    27     for(int i=1;i<=k;i++)
    28         for(int j=1;j<=len[i];j++){
    29             x++;
    30             int bl=(x-1)/blo+1;
    31             b[bl][++t]=a[i][j];
    32             if(t>=blo) t=0;
    33         }
    34     k=m/blo;
    35     if(!m%blo) k++;
    36     for(int i=1;i<k;i++){
    37         len[i]=blo;
    38         for(int j=1;j<=len[i];j++) a[i][j]=b[i][j];
    39     }
    40     if(!m%blo) len[k]=blo;
    41     else len[k]=m-blo*blo;
    42     for(int j=1;j<=len[k];j++) a[k][j]=b[k][j];    
    43 }
    44 
    45 int main()
    46 {
    47     n=read(),m=n;
    48     blo=sqrt(n);
    49     for(re int i=1;i<=n;i++){
    50         int x=read(),bl=(i-1)/blo+1;
    51         a[bl][++len[bl]]=x;
    52     }
    53     for(re int i=1;i<=n;i++){
    54         int opt=read(),l=read(),r=read(),c=read();
    55         if(!opt){
    56             new_point++;
    57             
    58             int x=0,t;
    59             for(t=1;t<=blo;t++){    //暴力找l的位置 
    60                 if(x+len[t]>=l) break;
    61                 x+=len[t];
    62             }
    63             
    64             len[t]++,l-=x;    //暴力插点 
    65             for(int i=len[t];i>l;i--) a[t][i]=a[t][i-1];
    66             a[t][l]=r;
    67             
    68             if(new_point>=blo) rebuild();    //重新分块
    69         }
    70         else {
    71             re int x=0,t;
    72             for(t=1;t<=blo;t++){
    73                 if(x+len[t]>=r) break;
    74                 else x+=len[t];
    75             }
    76             printf("%d
    ",a[t][r-x]);
    77         }
    78     }
    79     return 0;
    80 }
    View Code

      7. 区间乘法  区间加法  单点询问 

    • 乘法优先级高于加法的实现
     1 #include<cstdio>
     2 #include<cmath>
     3 #include<algorithm>
     4 #define N 100010
     5 #define mod 10007
     6 #define ll long long
     7 using namespace std;
     8 ll read(){
     9     ll x=0,f=1; char c=getchar();
    10     while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    11     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    12     return x*f;
    13 }
    14 int n,blo;
    15 int a[N],bl[N],atag[N],mtag[N];
    16 void reset(int x){
    17     for(int i=(x-1)*blo+1;i<=min(n,x*blo);i++)    //注意与n取min 
    18         a[i]=(a[i]*mtag[x]+atag[x])%mod;
    19         atag[x]=0,mtag[x]=1;
    20 }
    21 void solve(int opt,int l,int r,int c){
    22     reset(bl[l]);        //乘法优先级 
    23     for(int i=l;i<=min(r,bl[l]*blo);i++){
    24         if(opt==0) a[i]+=c;
    25         else a[i]*=c;
    26         a[i]%=mod;
    27     }
    28     if(bl[l]!=bl[r]){
    29         reset(bl[r]);        //乘法优先级
    30         for(int i=(bl[r]-1)*blo+1;i<=r;i++){
    31             if(opt==0) a[i]+=c;
    32             else a[i]*=c;
    33             a[i]%=mod;
    34         }
    35     }
    36     for(int i=bl[l]+1;i<=bl[r]-1;i++){
    37         if(opt==0) atag[i]=(atag[i]+c)%mod;
    38         else{
    39             atag[i]=(atag[i]*c)%mod;    // 注意 
    40             mtag[i]=(mtag[i]*c)%mod;
    41         }
    42     }
    43 }
    44 int main()
    45 {
    46     n=read(),blo=sqrt(n);
    47     for(int i=1;i<=n;i++){
    48         a[i]=read();
    49         bl[i]=(i-1)/blo+1;
    50     }
    51     for(int i=1;i<=bl[n];i++) mtag[i]=1;    //初始化mtag 
    52     for(int i=1;i<=n;i++){
    53         int opt=read(),l=read(),r=read(),c=read();
    54         if(opt==2) printf("%d
    ",(a[r]*mtag[bl[r]]+atag[bl[r]])%mod);
    55         else solve(opt,l,r,c);
    56     }     
    57     return 0;
    58 }
    View Code

       8. 区间询问等于一个数c的元素,并将这个区间的所有元素改为c 

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<algorithm>
     5 #define N 100010
     6 #define ll long long
     7 using namespace std;
     8 ll read(){
     9     ll x=0,f=1; char c=getchar();
    10     while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    11     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    12     return x*f;
    13 }
    14 int n,blo;
    15 int a[N],bl[N],tag[N];
    16 void reset(int x){
    17     if(tag[x]==-1) return ;
    18     for(int i=(x-1)*blo+1;i<=x*blo;i++)
    19         a[i]=tag[x];
    20     tag[x]=-1;
    21 }
    22 int solve(int l,int r,int c){
    23     int ans=0;
    24     reset(bl[l]);
    25     for(int i=l;i<=min(r,bl[l]*blo);i++){
    26         if(a[i]!=c) a[i]=c;
    27         else ans++;
    28     }
    29     if(bl[l]!=bl[r]){
    30         reset(bl[r]);
    31         for(int i=(bl[r]-1)*blo+1;i<=r;i++)
    32             if(a[i]!=c) a[i]=c;
    33             else ans++;
    34     }
    35     for(int i=bl[l]+1;i<=bl[r]-1;i++){
    36         if(tag[i]!=-1){
    37             if(tag[i]==c) ans+=blo;
    38             else tag[i]=c;
    39         }
    40         else{
    41             for(int j=(i-1)*blo+1;j<=i*blo;j++)
    42                 if(a[j]!=c) a[j]=c;
    43                 else ans++;
    44             tag[i]=c;
    45         }
    46     }
    47     return ans;
    48 }
    49 int main()
    50 {
    51     memset(tag,-1,sizeof(tag));
    52     n=read(),blo=sqrt(n);
    53     for(int i=1;i<=n;i++){
    54         a[i]=read();
    55         bl[i]=(i-1)/blo+1;
    56     }
    57     for(int i=1;i<=n;i++){
    58         int l=read(),r=read(),c=read();
    59         printf("%d
    ",solve(l,r,c));
    60     }
    61     return 0;
    62 }
    View Code

       9.  区间的最小众数 [ 未AC 88分 ]

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <cmath>
     5 #include <map>
     6 #include <vector>
     7 using namespace std;
     8 int pos[110000], v[110000], cnt[110000], a[110000];
     9 int id, n, N;
    10 int f[510][510];       //块i到j之间的最小众数的id
    11 map<int, int> m;       //记录每个数的id
    12 vector<int> b[51000];  //记录每个数出现的第一个位置
    13 void dp(int x) {
    14     memset(cnt, 0, sizeof(cnt));
    15     int maxx = 0, ss = 0;  //最大的id及最大的值
    16     for (int i = (x - 1) * N + 1; i <= n; i++) {
    17         int cc = ++cnt[a[i]];  //统计每个数出现的个数
    18         if (cc > ss || cc == ss && v[a[i]] < v[maxx]) {
    19             ss = cc;
    20             maxx = a[i];
    21         }
    22         f[x][pos[i]] = maxx;
    23     }
    24 }
    25 int along(int l, int r, int x)  // ask how long
    26 {
    27     int ans = upper_bound(b[x].begin(), b[x].end(), r) -
    28               lower_bound(b[x].begin(), b[x].end(), l);  //求l到r在x块的长度(r的后继-l的前驱)
    29     return ans;
    30 }
    31 int solve(int x, int y) {
    32     int ss = 0, maxx = 0;
    33     //完整的块
    34     maxx = f[pos[x] + 1][pos[y] - 1];
    35     ss = along(x, y, maxx);
    36     //不完整的块
    37     for (int i = x; i <= min(pos[x] * N, y); i++) {
    38         int cc = along(x, y, a[i]);
    39         if (cc > ss || cc == ss && v[a[i]] < v[maxx]) {
    40             ss = cc;
    41             maxx = a[i];
    42         }
    43     }
    44     if (pos[x] != pos[y]) {
    45         for (int i = (pos[y] - 1) * N + 1; i <= y; i++) {
    46             int cc = along(x, y, a[i]);
    47             if (cc > ss || cc == ss && v[a[i]] < v[maxx]) {
    48                 ss = cc;
    49                 maxx = a[i];
    50             }
    51         }
    52     }
    53     return maxx;
    54 }
    55 int main() {
    56     scanf("%d", &n);
    57     N = sqrt(n);
    58     for (int i = 1; i <= n; i++) {
    59         scanf("%d", &a[i]);
    60         pos[i] = (i - 1) / N + 1;
    61         if (m[a[i]] == 0) {
    62             m[a[i]] = ++id;  // a[i]是第几个出现的
    63             v[id] = a[i];
    64         }
    65         a[i] = m[a[i]];        //强制重新编号
    66         b[a[i]].push_back(i);  //记录每一个出现的位置
    67     }
    68     for (int i = 1; i <= pos[n]; i++) dp(i);
    69     for (int i = 1; i <= n; i++) {
    70         int x, y;
    71         scanf("%d%d", &x, &y);
    72         if (x > y)
    73             swap(x, y);
    74         printf("%d
    ", v[solve(x, y)]);
    75     }
    76     return 0;
    77 }
    View Code
  • 相关阅读:
    fork 函数 和vfork 函数的区别
    进程时间
    输出子进程状态
    二维字符串数组字典排序
    括号匹配
    队列实现二叉树层序遍历
    二叉查找树
    分块查找
    JS中的className含义
    Java打印温度转换表
  • 原文地址:https://www.cnblogs.com/RR-Jin/p/11623274.html
Copyright © 2011-2022 走看看