[分块]入门题目

https://loj.ac/problems/search?keyword=%E5%88%86%E5%9D%97

https://blog.csdn.net/keepcoral/article/details/80743559

数列分块入门 1

正确解法:

简单分块,然后区间之外的暴力,区间之内的 lazy 数组标记。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <set>
 7 #include <map>
 8 #include <vector>
 9 #include <cctype>
10 #include <sstream>
11 using namespace std;
12 typedef long long ll;
13 const int inf=0x7fffffff;
14 const int N=50000+100;
15 const int M=5000+10;
16 const double PI=acos(-1.0);
17 int n;
18 ll a[N],lazy[10000];
19 int op,l,r,c;
20 vector<ll>buck[10000];
21 int main()
22 {
23     scanf("%d",&n);
24     int b=sqrt(n),m=n;
25     //cout<<b<<endl;
26     for(int i=0;i<n;i++)
27     {
28         scanf("%d",&a[i]);
29         buck[i%b].push_back(a[i]);
30     }
31     while(m--)
32     {
33         scanf("%d %d %d %d",&op,&l,&r,&c);
34         if(op==0)
35         {
36             if(l>0)    l--;
37             while(l<r && l%b!=0)    a[l++]+=c;
38             while(l<r && r%b!=0)    a[--r]+=c;
39             while(l<r)
40             {
41                 int kk=l/b;
42                 lazy[kk]+=c;
43                 l+=b;
44             }
45         }
46         else
47         {
48             r--;
49             printf("%d
",a[r]+lazy[r/b]);
50         }
51     }
52 
53 
54     return 0;
55 }
View Code
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <set>
 7 #include <map>
 8 #include <vector>
 9 #include <cctype>
10 #include <sstream>
11 using namespace std;
12 typedef long long ll;
13 const int inf=0x7fffffff;
14 const int N=50000+100;
15 const int M=5000+10;
16 const double PI=acos(-1.0);
17 int n;
18 ll a[N],lazy[10000];
19 int op,l,r,c;
20 vector<ll>buck[10000];
21 int main()
22 {
23     scanf("%d",&n);
24     int b=sqrt(n),m=n;
25     //cout<<b<<endl;
26     for(int i=0;i<n;i++)
27     {
28         scanf("%d",&a[i]);
29         buck[i%b].push_back(a[i]);
30     }
31     while(m--)
32     {
33         scanf("%d %d %d %d",&op,&l,&r,&c);
34         if(op==0)
35         {
36             if(l>0)    l--;//因为数组从0开始
37             while(l<r && l%b!=0)    a[l++]+=c;//左边多余的
38             while(l<r && r%b!=0)    a[--r]+=c;//右边多余的
39             while(l<r)//整块
40             {
41                 int kk=l/b;
42                 lazy[kk]+=c;
43                 l+=b;
44             }
45         }
46         else
47         {
48             r--;
49             printf("%d
",a[r]+lazy[r/b]);
50         }
51     }
52 
53 
54     return 0;
55 }
View Code

 数列分块入门 2

正确解法:

把所有都存进数组vector里面, 然后排序,如果改变一个分块里面的部分值,就再次对块进行更新。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <set>
 7 #include <map>
 8 #include <vector>
 9 #include <cctype>
10 #include <sstream>
11 using namespace std;
12 typedef long long ll;
13 const int inf=0x7fffffff;
14 const int N=50000+100;
15 const int M=5000+10;
16 const double PI=acos(-1.0);
17 int n;
18 ll a[N],lazy[10000];
19 int blo[N];
20 int op,l,r,c,b;
21 vector<ll>buck[10000];
22 void reset(int x)
23 {
24     buck[x].clear();
25     for(int i=(x-1)*b+1;i<=min(x*b,n);i++)
26         buck[x].push_back(a[i]);
27     sort(buck[x].begin(),buck[x].end());
28 }
29 void add(int l,int r,int c)
30 {
31     for(int i=l;i<=min(blo[l]*b,r);i++)
32         a[i]+=c;
33     reset(blo[l]);
34     if(blo[l]!=blo[r])
35     {
36         for(int i=(blo[r]-1)*b+1;i<=r;i++)
37             a[i]+=c;
38         reset(blo[r]);
39     }
40     for(int i=blo[l]+1;i<=blo[r]-1;i++)
41         lazy[i]+=c;
42 }
43 int query(int l,int r,int c)
44 {
45     int ans=0;
46     for(int i=l;i<=min(blo[l]*b,r);i++)
47         if(a[i]+lazy[blo[l]]<c) ans++;
48     if(blo[l]!=blo[r])
49     {
50         for(int i=(blo[r]-1)*b+1;i<=r;i++)
51             if(a[i]+lazy[blo[r]]<c) ans++;
52     }
53     for(int i=blo[l]+1;i<=blo[r]-1;i++)
54         ans+=lower_bound(buck[i].begin(),buck[i].end(),c-lazy[i])-buck[i].begin();
55     return ans;
56 }
57 int main()
58 {
59     scanf("%d",&n);
60     b=sqrt(n);
61     for(int i=1;i<=n;i++)
62     {
63         scanf("%lld",&a[i]);
64         blo[i]=(i-1)/b+1;
65         buck[blo[i]].push_back(a[i]);
66     }
67     for(int i=1;i<=blo[n];i++)
68         sort(buck[i].begin(),buck[i].end());
69     for(int i=1;i<=n;i++)
70     {
71         scanf("%d %d %d %d",&op,&l,&r,&c);
72         if(op==0)   add(l,r,c);
73         else
74             printf("%d
",query(l,r,c*c));
75     }
76 
77 
78     return 0;
79 }
View Code

数列分块入门 3

正确解法:

这个需要找相对应的元素,而不是元素个数,就用到了set。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <set>
 7 #include <map>
 8 #include <vector>
 9 #include <cctype>
10 #include <sstream>
11 using namespace std;
12 typedef long long ll;
13 const int inf=0x7fffffff;
14 const int N=100000+100;
15 const int M=5000+10;
16 const double PI=acos(-1.0);
17 int n;
18 ll a[N],lazy[10000];
19 int blo[N];
20 int op,l,r,c,m;
21 set<ll>buck[10000];
22 void reset(int x)
23 {
24     buck[x].clear();
25     for(int i=(x-1)*m+1;i<=min(x*m,n);i++)
26         buck[x].insert(a[i]);
27 }
28 void add(int l,int r,int c)
29 {
30     for(int i=l;i<=min(blo[l]*m,r);i++)
31         a[i]+=c;
32     reset(blo[l]);
33     if(blo[l]!=blo[r])
34     {
35         for(int i=(blo[r]-1)*m+1;i<=r;i++)
36             a[i]+=c;
37         reset(blo[r]);
38     }
39     for(int i=blo[l]+1;i<=blo[r]-1;i++)
40         lazy[i]+=c;
41 }
42 ll query(int l,int r,int c)
43 {
44     ll kk=-1;
45     for(int i=l;i<=min(blo[l]*m,r);i++)
46         if(a[i]+lazy[blo[l]]<c)
47             kk=max(kk,a[i]+lazy[blo[l]]);
48     if(blo[l]!=blo[r])
49     {
50         for(int i=(blo[r]-1)*m+1;i<=r;i++)
51             if(a[i]+lazy[blo[r]]<c)
52                 kk=max(kk,a[i]+lazy[blo[r]]);
53     }
54     for(int i=blo[l]+1;i<=blo[r]-1;i++)
55     {
56         set<ll>::iterator it;
57         it=buck[i].lower_bound(c-lazy[i]);
58         if(it==buck[i].begin()) continue;
59         it--;
60         kk=max(kk,(*it)+lazy[i]);
61     }
62     return kk;
63 }
64 int main()
65 {
66     scanf("%d",&n);
67     m=sqrt(n);
68     for(int i=1;i<=n;i++)
69     {
70         scanf("%lld",&a[i]);
71         blo[i]=(i-1)/m+1;
72         buck[blo[i]].insert(a[i]);
73     }
74     for(int i=1;i<=n;i++)
75     {
76         scanf("%d %d %d %d",&op,&l,&r,&c);
77         if(op==0)   add(l,r,c);
78         else
79             printf("%lld
",query(l,r,c));
80     }
81 
82 
83     return 0;
84 }
View Code

数列分块入门 4

正确解法:

开一个sum数组就好了,注意的是 整个块相加是 sum[i]+lazy[i]*m;

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <set>
 7 #include <map>
 8 #include <vector>
 9 #include <cctype>
10 #include <sstream>
11 using namespace std;
12 typedef long long ll;
13 const int inf=0x7fffffff;
14 const int N=100000+100;
15 const int M=5000+10;
16 const double PI=acos(-1.0);
17 int n;
18 ll a[N],lazy[10000],sum[10000];
19 int blo[N];
20 int op,l,r,c,m;
21 set<ll>buck[10000];
22 void reset(int x)
23 {
24     buck[x].clear();
25     for(int i=(x-1)*m+1;i<=min(x*m,n);i++)
26         buck[x].insert(a[i]);
27 }
28 void add(int l,int r,int c)
29 {
30     for(int i=l;i<=min(blo[l]*m,r);i++)
31         a[i]+=c;
32     sum[blo[l]]+=(min(blo[l]*m,r)-l+1)*c;
33     //reset(blo[l]);
34     if(blo[l]!=blo[r])
35     {
36         for(int i=(blo[r]-1)*m+1;i<=r;i++)
37             a[i]+=c;
38         sum[blo[r]]+=(r-(blo[r]-1)*m)*c;
39         //reset(blo[r]);
40     }
41     for(int i=blo[l]+1;i<=blo[r]-1;i++)
42         lazy[i]+=c;
43 }
44 ll query(int l,int r,int c)
45 {
46     ll ans=0;
47     for(int i=l;i<=min(blo[l]*m,r);i++)
48         ans+=(a[i]+lazy[blo[l]])%c;
49     ans%=c;
50     if(blo[l]!=blo[r])
51     {
52         for(int i=(blo[r]-1)*m+1;i<=r;i++)
53             ans+=(a[i]+lazy[blo[r]])%c;
54         ans%=c;
55     }
56     for(int i=blo[l]+1;i<=blo[r]-1;i++)
57     {
58         ans+=(sum[i]+lazy[i]*m)%c;
59         ans%=c;
60     }
61     return ans;
62 }
63 int main()
64 {
65     scanf("%d",&n);
66     m=sqrt(n);
67     for(int i=1;i<=n;i++)
68     {
69         scanf("%lld",&a[i]);
70         blo[i]=(i-1)/m+1;
71         buck[blo[i]].insert(a[i]);
72         sum[blo[i]]+=a[i];
73     }
74     for(int i=1;i<=n;i++)
75     {
76         scanf("%d %d %d %d",&op,&l,&r,&c);
77         if(op==0)   add(l,r,c);
78         else
79             printf("%lld
",query(l,r,c+1));
80     }
81 
82 
83     return 0;
84 }
View Code

数列分块入门 5

正确解法:

每个数最终都会变成0或1,我们暴力每个块,若它的最大值小于等于1,就不必再更新,否则就暴力更新。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <set>
 7 #include <map>
 8 #include <vector>
 9 #include <cctype>
10 #include <sstream>
11 using namespace std;
12 typedef long long ll;
13 const int inf=0x7fffffff;
14 const int N=100000+100;
15 const int M=5000+10;
16 const double PI=acos(-1.0);
17 int n;
18 ll a[N],lazy[10000],sum[10000],maxx[10000];
19 int blo[N];
20 int op,l,r,c,m;
21 set<ll>buck[10000];
22 void reset(int x)
23 {
24     buck[x].clear();
25     for(int i=(x-1)*m+1;i<=min(x*m,n);i++)
26         buck[x].insert(a[i]);
27 }
28 void rset(int x)
29 {
30     sum[x]=0;
31     maxx[x]=0;
32     for(int i=(x-1)*m+1;i<=min(x*m,n);i++)
33     {
34         sum[x]+=a[i];
35         maxx[x]=max(maxx[x],a[i]);
36     }
37 }
38 void add(int l,int r,int c)
39 {
40     for(int i=l;i<=min(blo[l]*m,r);i++)
41         a[i]=sqrt(a[i]);
42     rset(blo[l]);
43     //reset(blo[l]);
44     if(blo[l]!=blo[r])
45     {
46         for(int i=(blo[r]-1)*m+1;i<=r;i++)
47             a[i]=sqrt(a[i]);
48         rset(blo[r]);
49         //reset(blo[r]);
50     }
51     for(int i=blo[l]+1;i<=blo[r]-1;i++)
52         if(maxx[i]>1)
53         {
54             for(int j=(i-1)*m+1;j<=i*m;j++)
55                 a[j]=sqrt(a[j]);
56             rset(i);
57         }
58 }
59 ll query(int l,int r,int c)
60 {
61     ll ans=0;
62     for(int i=l;i<=min(blo[l]*m,r);i++)
63         ans+=a[i];
64     if(blo[l]!=blo[r])
65     {
66         for(int i=(blo[r]-1)*m+1;i<=r;i++)
67             ans+=a[i];
68     }
69     for(int i=blo[l]+1;i<=blo[r]-1;i++)
70     {
71         ans+=sum[i];
72     }
73     return ans;
74 }
75 int main()
76 {
77     scanf("%d",&n);
78     m=sqrt(n);
79     for(int i=1;i<=n;i++)
80     {
81         scanf("%lld",&a[i]);
82         blo[i]=(i-1)/m+1;
83         sum[blo[i]]+=a[i];
84         maxx[blo[i]]=2;
85     }
86     for(int i=1;i<=n;i++)
87     {
88         scanf("%d %d %d %d",&op,&l,&r,&c);
89         if(op==0)   add(l,r,c);
90         else
91             printf("%lld
",query(l,r,c));
92     }
93 
94 
95     return 0;
96 }
View Code

数列分块入门 6

正确解法:

 1 pair<int, int> query(int a) {
 2     int i = 1;
 3     while (a > ve[i].size()) {
 4         a -= ve[i].size();
 5         i++;
 6     }
 7     return make_pair(i, a - 1);
 8 }
 9 
10 pair<int, int> t = query(b);
11 printf("%d
", ve[t.first][t.second]);
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <string>
 5 #include <queue>
 6 #include <algorithm>
 7 #include <vector>
 8 #include <cmath>
 9 #include <set>
10 #include <map>
11 #define INF 99999999
12 typedef long long ll;
13 using namespace std;
14 int v[100999];     //原数值
15 int blo[100999];   //块的数组
16 int atag[100999];  //每一个块的标记数组
17 int n, m, last;
18 int temp[200099];
19 vector<int> ve[1009];
20 pair<int, int> query(int a) {
21     int i = 1;
22     while (a > ve[i].size()) {
23         a -= ve[i].size();
24         i++;
25     }
26     return make_pair(i, a - 1);
27 }
28 void reset() {
29     int top = 1, i;
30     for (i = 1; i <= last; i++) {
31         for (vector<int>::iterator it = ve[i].begin(); it != ve[i].end(); it++) {
32             temp[top++] = *it;
33         }
34         ve[i].clear();
35     }
36     m = sqrt(top - 1);
37     for (i = 1; i <= top - 1; i++) {
38         blo[i] = (i - 1) / m + 1;
39         ve[blo[i]].push_back(temp[i]);
40     }
41     m = blo[top - 1];
42 }
43 void insert(int a, int b) {
44     pair<int, int> t = query(a);  //
45     ve[t.first].insert(ve[t.first].begin() + t.second, b);
46     if (ve[t.first].size() > m * m)
47         reset();
48 }
49 int main() {
50     int i;
51     scanf("%d", &n);
52     m = sqrt(n);
53     for (i = 1; i <= n; i++) {
54         scanf("%d", &v[i]);
55         blo[i] = (i - 1) / m + 1;
56         ve[blo[i]].push_back(v[i]);
57     }
58     last = blo[n];
59     for (i = 1; i <= n; i++) {
60         int op, a, b, c;
61         scanf("%d%d%d%d", &op, &a, &b, &c);
62         if (op == 0)
63             insert(a, b);
64         else {
65             pair<int, int> t = query(b);
66             printf("%d
", ve[t.first][t.second]);
67         }
68     }
69 }
View Code

插入就用 make pair 查找位置,然后vector的动态插入。

如果某个块特别大,那就所有全部拆开重分。

数列分块入门 7

正确解法:

我们让乘法标记的优先级高于加法(如果反过来的话,新的加法标记无法处理)

若当前的一个块乘以m1后加上a1,这时进行一个乘m2的操作,则原来的标记变成m1*m2,a1*m2

若当前的一个块乘以m1后加上a1,这时进行一个加a2的操作,则原来的标记变成m1,a1+a2

如果是单独一个数的话,就把标记更新进去,然后再更新这个数。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <set>
 7 #include <map>
 8 #include <vector>
 9 #include <cctype>
10 #include <sstream>
11 using namespace std;
12 typedef long long ll;
13 const int inf=0x7fffffff;
14 const int N=100000+100;
15 const int M=50000+10;
16 const int MOD=10007;
17 const double PI=acos(-1.0);
18 int n,m,a[N];
19 int lazy[M],blo[N],che[M];
20 int op,l,r,c;
21 vector<int>ve[M];
22 void reset(int x)
23 {
24     for(int i=(blo[x]-1)*m+1;i<=min(blo[x]*m,n);i++)
25         a[i]=(a[i]*che[blo[x]]+lazy[blo[x]])%MOD;
26     che[blo[x]]=1;
27     lazy[blo[x]]=0;
28 }
29 void add(int l,int r,int c)
30 {
31     reset(l);
32     for(int i=l;i<=min(blo[l]*m,r);i++)
33     {
34         if(op==0)   a[i]+=c;
35         else if(op==1)  a[i]*=c;
36         a[i]%=MOD;
37     }
38     if(blo[l]!=blo[r])
39     {
40         reset(r);
41         for(int i=(blo[r]-1)*m+1;i<=r;i++)
42         {
43             if(op==0)   a[i]+=c;
44             else if(op==1)  a[i]*=c;
45             a[i]%=MOD;
46         }
47     }
48     for(int i=blo[l]+1;i<=blo[r]-1;i++)
49     {
50         if(op==0)
51         {
52             lazy[i]+=c;
53         }
54         else if(op==1)
55         {
56             che[i]*=c;
57             che[i]%=MOD;
58             lazy[i]*=c;
59             lazy[i]%=MOD;
60         }
61     }
62 }
63 int query(int r)
64 {
65     return (a[r]*che[blo[r]]+lazy[blo[r]])%MOD;
66 }
67 int main()
68 {
69     scanf("%d",&n);
70     m=sqrt(n);
71     for(int i=1;i<=n;i++)
72     {
73         scanf("%d",&a[i]);
74         blo[i]=(i-1)/m+1;
75         ve[blo[i]].push_back(a[i]);
76         che[blo[i]]=1;
77     }
78     for(int i=1;i<=n;i++)
79     {
80         scanf("%d %d %d %d",&op,&l,&r,&c);
81         if(op==0||op==1)
82             add(l,r,c);
83         else
84             printf("%d
",query(r));
85     }
86 
87 
88     return 0;
89 }
View Code

数列分块入门 8

正确解法:

先把每个块标记为-1,证明里面元素都不同。

如果元素相同的话,先更新数组,然后对单个点更新。

对于整个块来说,如果每个元素都相同的话,就看是否为c,是的话 ans+=m,不是的话,标记为c

若不同的话,就暴力分析。并标记为c。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <set>
 7 #include <map>
 8 #include <vector>
 9 #include <cctype>
10 #include <sstream>
11 using namespace std;
12 typedef long long ll;
13 const int inf=0x7fffffff;
14 const int N=100000+100;
15 const int M=50000+10;
16 const int MOD=10007;
17 const double PI=acos(-1.0);
18 int n,m,a[N];
19 int lazy[M],blo[N],che[M];
20 int op,l,r,c;
21 vector<int>ve[M];
22 void reset(int x)
23 {
24     for(int i=(x-1)*m+1;i<=min(x*m,n);i++)
25         a[i]=lazy[x];
26     lazy[x]=-1;
27 }
28 int add(int l,int r,int c)
29 {
30     int ans=0;
31     if(lazy[blo[l]]!=-1)    reset(blo[l]);
32     for(int i=l;i<=min(blo[l]*m,r);i++)
33     {
34         if(a[i]==c) ans++;
35         else a[i]=c;
36     }
37     if(blo[l]!=blo[r])
38     {
39         if(lazy[blo[r]]!=-1)    reset(blo[r]);
40         for(int i=(blo[r]-1)*m+1;i<=r;i++)
41         {
42             if(a[i]==c) ans++;
43             else a[i]=c;
44         }
45     }
46     for(int i=blo[l]+1;i<=blo[r]-1;i++)
47     {
48         if(lazy[i]!=-1)
49         {
50             if(lazy[i]==c)  ans+=m;
51             else    lazy[i]=c;
52         }
53         else
54         {
55             for(int j=(i-1)*m+1;j<=i*m;j++)
56             {
57                 if(a[j]==c) ans++;
58                 else a[j]=c;
59             }
60             lazy[i]=c;
61         }
62     }
63     return ans;
64 }
65 int main()
66 {
67     scanf("%d",&n);
68     m=sqrt(n);
69     for(int i=1;i<=n;i++)
70     {
71         scanf("%d",&a[i]);
72         blo[i]=(i-1)/m+1;
73         ve[blo[i]].push_back(a[i]);
74         lazy[blo[i]]=-1;
75     }
76     for(int i=1;i<=n;i++)
77     {
78         scanf("%d %d %d",&l,&r,&c);
79         printf("%d
",add(l,r,c));
80     }
81 
82 
83     return 0;
84 }
View Code
No matter how you feel, get up , dress up , show up ,and never give up.
原文地址:https://www.cnblogs.com/Kaike/p/10838141.html