两个分块题

题目一:给出一个长为 的数列,以及 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 }

    

原文地址:https://www.cnblogs.com/lnu161403214/p/9287150.html