• 两个分块题


    题目一:给出一个长为 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。

     分析  :将数列分成sqrt(n)块,每块维护一个懒惰标记、一个有序表(vector实现)即可。

        操作的时候,先暴力处理左右两端不完整的块,再处理中间完整的块,注意懒惰标记的释放。

        区间加法O(√n),询问区间内小于某个值 x 的元素个数O(√n*log√n)

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstdlib>
     5 #include<algorithm>
     6 #include<cstring>
     7 #include<string>
     8 #include<vector>
     9 #include<map>
    10 #include<set>
    11 #include<queue>
    12 #define N 100005
    13 using namespace std;
    14 typedef long long ll;
    15 int a[N],b[N];
    16 int mark[505];
    17 vector<int>vt[505];
    18 int n,blo;
    19 void resort(int x)
    20 {
    21     vt[x].clear();
    22     for(int i=blo*(x-1)+1;i<=min(n,blo*x);i++)
    23         vt[x].push_back(a[i]);
    24     sort(vt[x].begin(),vt[x].end());
    25 }
    26 int main()
    27 {
    28    
    29    cin>>n;
    30    blo=sqrt(n);
    31     for(int i=1;i<=n;i++)
    32        scanf("%d",&a[i]);
    33     for(int i=1;i<=n;i++)
    34     {
    35         b[i]=(i-1)/blo+1;
    36         vt[b[i]].push_back(a[i]);
    37     }
    38     for(int i=1;i<=b[n];i++)
    39     {
    40         sort(vt[i].begin(),vt[i].end());
    41     }
    42     for(int i=1;i<=n;i++)
    43     {
    44         int op,l,r,c;
    45         scanf("%d%d%d%d",&op,&l,&r,&c);
    46         if(op==0)
    47         {
    48             for(int i=l;i<=min(blo*b[l],r);i++)
    49                 a[i]+=c;
    50             resort(b[l]);
    51             for(int i=b[l]+1;i<=b[r]-1;i++)
    52             {
    53                 mark[i]+=c;
    54             }
    55             if(b[l]!=b[r])
    56             {
    57                 for(int i=blo*(b[r]-1)+1;i<=r;i++)
    58                     a[i]+=c;
    59                 resort(b[r]);
    60             }
    61         }
    62         else if(op==1)
    63         {    int num=0;
    64             ll cc=c*c;
    65             for(int i=l;i<=min(blo*b[l],r);i++)
    66                 if(cc>a[i]+mark[b[l]]) num++;
    67     
    68             for(int i=b[l]+1;i<=b[r]-1;i++)
    69             {
    70                 num+=lower_bound(vt[i].begin(),vt[i].end(),cc-mark[i])-vt[i].begin();
    71             }
    72     
    73             if(b[l]!=b[r])
    74             {
    75                 for(int i=blo*(b[r]-1)+1;i<=r;i++)
    76                     if(cc>a[i]+mark[b[r]]) num++;
    77             }
    78             printf("%d
    ",num);
    79         }
    80     }     
    81     return 0;
    82 } 

    题目二:给出一个长为 n 的数列,以及 n 个操作,操作涉及单点插入,单点询问,数据随机生成。

     分析 :将数列分成sqrt(n)块,每块维护一个链表。由于单点插入会使原来的编号发生改变,所以每块还需维护好该块的区间(l,r)。

        查找的时候先遍历找到属于哪个块,再在块中遍历即可,单次O(√n)。

        一直插入操作可能使某个块过长,这时需要将其切成两个块(重构)

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstdlib>
     5 #include<algorithm>
     6 #include<cstring>
     7 #include<string>
     8 #include<vector>
     9 #include<map>
    10 #include<set>
    11 #include<queue>
    12 #include<list>
    13 #define N 100005
    14 using namespace std;
    15 typedef long long ll;
    16 int a[N];
    17 
    18 typedef struct NODE{
    19     int l, r;
    20     list<int>lt;
    21 }node;
    22 list<node>head(405);
    23 int main()
    24 {
    25     int n,blo;
    26     cin>>n;
    27     blo=sqrt(n);
    28     for(int i=1;i<=n;i++)
    29         scanf("%d",&a[i]);
    30     int m=(n-1)/blo+1;
    31     list<node>::iterator p=head.begin();
    32     for(int i=1;i<=m;i++)
    33     {    
    34         p->l=(i-1)*blo+1;
    35         p->r=min(n,i*blo);
    36         for(int k=p->l;k<=p->r;k++)
    37         {
    38             p->lt.push_back(a[k]);
    39         }
    40         p++;    
    41     }
    42     for(int i=1;i<=n;i++)
    43     {
    44         int op,l,r,c;
    45         scanf("%d%d%d%d",&op,&l,&r,&c);
    46         if(op==0)
    47         {
    48             p=head.begin();
    49             while(p!=head.end())
    50             {
    51                 if(p->l<=l&&l<=p->r) break;
    52                 p++;
    53             }
    54             int tot=0;
    55             list<int>::iterator p2=p->lt.begin(); 
    56             while(p2!=p->lt.end())
    57             {
    58                 if(p->l+tot==l) {p->lt.insert(p2,r);break;}
    59                 p2++;
    60                 tot++;
    61             }
    62             p->r+=1;
    63             p++;
    64             for(;p!=head.end();p++)
    65             {
    66                 p->l+=1,p->r+=1;
    67             }
    68         }
    69         else if(op==1)
    70         {
    71             p=head.begin();
    72             while(p!=head.end())
    73             {
    74                 if(p->l<=r&&r<=p->r) break;
    75                 p++;
    76             }
    77             int tot=0;
    78             list<int>::iterator p2=p->lt.begin(); 
    79             while(p2!=p->lt.end())
    80             {
    81                 if(p->l+tot==r) {printf("%d
    ",*p2);break;}
    82                 p2++;
    83                 tot++;
    84             }
    85         }
    86         
    87         
    88     }
    89     
    90     return 0;
    91 }

        

  • 相关阅读:
    Ubuntu linux 关机、重启、注销 命令
    通过 URL 协议实现从 Safari 等浏览器中跳转打开你的 app
    (暴力调试控的福音)在ios iphone编程中使用封装的NSLog来打印调试信息
    mac终端关机命令
    iPhone5采用的下一代incell显示屏五大优势
    拉丁方阵
    OD使用教程16 调试篇16
    线性表14 数据结构和算法19
    带环单链表
    OD使用教程16 调试篇16
  • 原文地址:https://www.cnblogs.com/lnu161403214/p/9287150.html
Copyright © 2020-2023  润新知