「分块」数列分块入门1 – 9 by hzwer
由于 CH 回档导致原题面丢失,感谢诸暨海亮高级中学帮助重写了题面
已上传至 LOJ
由于每道题题面太长,限于篇幅,只给出大意,具体题目见小组内赛题,代码附在文末
可能涉及的几个词语解释:
区间:数列中连续一段的元素
区间操作:将某个区间[a,b]的所有元素进行某种改动的操作
块:我们将数列划分成若干个不相交的区间,每个区间称为一个块
整块:在一个区间操作时,完整包含于区间的块
不完整的块:在一个区间操作时,只有部分包含于区间的块,即区间左右端点所在的两个块
分块入门 1 by hzwer
给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查值。
这是一道能用许多数据结构优化的经典题,可以用于不同数据结构训练。
数列分块就是把数列中每m个元素打包起来,达到优化算法的目的。
以此题为例,如果我们把每m个元素分为一块,共有n/m块,每次区间加的操作会涉及O(n/m)个整块,以及区间两侧两个不完整的块中至多2m个元素。
我们给每个块设置一个加法标记(就是记录这个块中元素一起加了多少),每次操作对每个整块直接O(1)标记,而不完整的块由于元素比较少,暴力修改元素的值。
每次询问时返回元素的值加上其所在块的加法标记。
这样每次操作的复杂度是O(n/m)+O(m),根据均值不等式,当m取√n时总复杂度最低,为了方便,我们都默认下文的分块大小为√n。
分块入门 2 by hzwer
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。
有了上一题的经验,我们可以发现,数列简单分块问题实际上有三项东西要我们思考:
对于每次区间操作:
1.不完整的块 的O(√n)个元素怎么处理?
2.O(√n)个 整块 怎么处理?
3.要预处理什么信息(复杂度不能超过后面的操作)?
我们先来思考只有询问操作的情况,不完整的块枚举统计即可;而要在每个整块内寻找小于一个值的元素数,于是我们不得不要求块内元素是有序的,这样就能使用二分法对块内查询,需要预处理时每块做一遍排序,复杂度O(nlogn),每次查询在√n个块内二分,以及暴力2√n个元素,总复杂度O(nlogn + n√nlog√n)。
可以通过均值不等式计算出更优的分块大小,就不展开讨论了
那么区间加怎么办呢?
套用第一题的方法,维护一个加法标记,略有区别的地方在于,不完整的块修改后可能会使得该块内数字乱序,所以头尾两个不完整块需要重新排序,复杂度分析略。
在加法标记下的询问操作,块外还是暴力,查询小于(x – 加法标记)的元素个数,块内用(x – 加法标记)作为二分的值即可。
分块入门 3 by hzwer
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(比其小的最大元素)。
n<=100000其实是为了区分暴力和一些常数较大的写法。
接着第二题的解法,其实只要把块内查询的二分稍作修改即可。
不过这题其实想表达:可以在块内维护其它结构使其更具有拓展性,比如放一个 set ,这样如果还有插入、删除元素的操作,会更加的方便。
分块的调试检测技巧:
可以生成一些大数据,然后用两份分块大小不同的代码来对拍,还可以根据运行时间尝试调整分块大小,减小常数。
分块入门 4 by hzwer
给出一个长为n的数列,以及n个操作,操作涉及区间加法,区间求和。
这题的询问变成了区间上的询问,不完整的块还是暴力;而要想快速统计完整块的答案,需要维护每个块的元素和,先要预处理一下。
考虑区间修改操作,不完整的块直接改,顺便更新块的元素和;完整的块类似之前标记的做法,直接根据块的元素和所加的值计算元素和的增量。
分块入门 5 by hzwer
给出一个长为n的数列,以及n个操作,操作涉及区间开方,区间求和。
稍作思考可以发现,开方操作比较棘手,主要是对于整块开方时,必须要知道每一个元素,才能知道他们开方后的和,也就是说,难以快速对一个块信息进行更新。
看来我们要另辟蹊径。不难发现,这题的修改就只有下取整开方,而一个数经过几次开方之后,它的值就会变成 0 或者 1。
如果每次区间开方只不涉及完整的块,意味着不超过2√n个元素,直接暴力即可。
如果涉及了一些完整的块,这些块经过几次操作以后就会都变成 0 / 1,于是我们采取一种分块优化的暴力做法,只要每个整块暴力开方后,记录一下元素是否都变成了 0 / 1,区间修改时跳过那些全为 0 / 1 的块即可。
这样每个元素至多被开方不超过4次,显然复杂度没有问题。
分块入门 6 by hzwer
给出一个长为n的数列,以及n个操作,操作涉及单点插入,单点询问,数据随机生成。
先说随机数据的情况
之前提到过,如果我们块内用数组以外的数据结构,能够支持其它不一样的操作,比如此题每块内可以放一个动态的数组,每次插入时先找到位置所在的块,再暴力插入,把块内的其它元素直接向后移动一位,当然用链表也是可以的。
查询的时候类似,复杂度分析略。
但是这样做有个问题,如果数据不随机怎么办?
如果先在一个块有大量单点插入,这个块的大小会大大超过√n,那块内的暴力就没有复杂度保证了。
还需要引入一个操作:重新分块(重构)
每根号n次插入后,重新把数列平均分一下块,重构需要的复杂度为O(n),重构的次数为√n,所以重构的复杂度没有问题,而且保证了每个块的大小相对均衡。
当然,也可以当某个块过大时重构,或者只把这个块分成两半。
分块入门 7 by hzwer
给出一个长为n的数列,以及n个操作,操作涉及区间乘法,区间加法,单点询问。
很显然,如果只有区间乘法,和分块入门 1 的做法没有本质区别,但要思考如何同时维护两种标记。
我们让乘法标记的优先级高于加法(如果反过来的话,新的加法标记无法处理)
若当前的一个块乘以m1后加上a1,这时进行一个乘m2的操作,则原来的标记变成m1*m2,a1*m2
若当前的一个块乘以m1后加上a1,这时进行一个加a2的操作,则原来的标记变成m1,a1+a2
分块入门 8 by hzwer
给出一个长为n的数列,以及n个操作,操作涉及区间询问等于一个数c的元素,并将这个区间的所有元素改为c。
区间修改没有什么难度,这题难在区间查询比较奇怪,因为权值种类比较多,似乎没有什么好的维护方法。
模拟一些数据可以发现,询问后一整段都会被修改,几次询问后数列可能只剩下几段不同的区间了。
我们思考这样一个暴力,还是分块,维护每个分块是否只有一种权值,区间操作的时候,对于同权值的一个块就O(1)统计答案,否则暴力统计答案,并修改标记,不完整的块也暴力。
这样看似最差情况每次都会耗费O(n)的时间,但其实可以这样分析:
假设初始序列都是同一个值,那么查询是O(√n),如果这时进行一个区间操作,它最多破坏首尾2个块的标记,所以只能使后面的询问至多多2个块的暴力时间,所以均摊每次操作复杂度还是O(√n)。
换句话说,要想让一个操作耗费O(n)的时间,要先花费√n个操作对数列进行修改。
初始序列不同值,经过类似分析后,就可以放心的暴力啦。
分块入门 9 by hzwer
给出一个长为n的数列,以及n个操作,操作涉及询问区间的最小众数。
这是一道经典难题,其实可以支持修改操作,具体见陈立杰大神的区间众数解题报告。
而且不强制在线的话有很多做法,可以看我blog一道类似题目 czy的后宫3
bzoj2724 是道强制在线区间众数,而且题目背景写的不错,这道题的题解就贴传送门咯
程序
分块入门1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define mod 998244353 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,blo; int v[50005],bl[50005],atag[50005]; void add(int a,int b,int c) { for(int i=a;i<=min(bl[a]*blo,b);i++) v[i]+=c; if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) v[i]+=c; for(int i=bl[a]+1;i<=bl[b]-1;i++) atag[i]+=c; } int main() { n=read();blo=sqrt(n); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++)bl[i]=(i-1)/blo+1; for(int i=1;i<=n;i++) { int f=read(),a=read(),b=read(),c=read(); if(f==0)add(a,b,c); if(f==1)printf("%d\n",v[b]+atag[bl[b]]); } return 0; } |
分块入门2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define mod 998244353 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,blo; int v[50005],bl[50005],atag[50005]; vector<int>ve[505]; void reset(int x) { ve[x].clear(); for(int i=(x-1)*blo+1;i<=min(x*blo,n);i++) ve[x].push_back(v[i]); sort(ve[x].begin(),ve[x].end()); } void add(int a,int b,int c) { for(int i=a;i<=min(bl[a]*blo,b);i++) v[i]+=c; reset(bl[a]); if(bl[a]!=bl[b]) { for(int i=(bl[b]-1)*blo+1;i<=b;i++) v[i]+=c; reset(bl[b]); } for(int i=bl[a]+1;i<=bl[b]-1;i++) atag[i]+=c; } int query(int a,int b,int c) { int ans=0; for(int i=a;i<=min(bl[a]*blo,b);i++) if(v[i]+atag[bl[a]]<c)ans++; if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) if(v[i]+atag[bl[b]]<c)ans++; for(int i=bl[a]+1;i<=bl[b]-1;i++) { int x=c-atag[i]; ans+=lower_bound(ve[i].begin(),ve[i].end(),x)-ve[i].begin(); } return ans; } int main() { n=read();blo=sqrt(n); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++) { bl[i]=(i-1)/blo+1; ve[bl[i]].push_back(v[i]); } for(int i=1;i<=bl[n];i++) sort(ve[i].begin(),ve[i].end()); for(int i=1;i<=n;i++) { int f=read(),a=read(),b=read(),c=read(); if(f==0)add(a,b,c); if(f==1)printf("%d\n",query(a,b,c*c)); } return 0; } |
分块入门3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define mod 998244353 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,blo; int v[100005],bl[100005],atag[100005]; set<int>st[105]; void add(int a,int b,int c) { for(int i=a;i<=min(bl[a]*blo,b);i++) { st[bl[a]].erase(v[i]); v[i]+=c; st[bl[a]].insert(v[i]); } if(bl[a]!=bl[b]) { for(int i=(bl[b]-1)*blo+1;i<=b;i++) { st[bl[b]].erase(v[i]); v[i]+=c; st[bl[b]].insert(v[i]); } } for(int i=bl[a]+1;i<=bl[b]-1;i++) atag[i]+=c; } int query(int a,int b,int c) { int ans=-1; for(int i=a;i<=min(bl[a]*blo,b);i++) { int val=v[i]+atag[bl[a]]; if(val<c)ans=max(val,ans); } if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) { int val=v[i]+atag[bl[b]]; if(val<c)ans=max(val,ans); } for(int i=bl[a]+1;i<=bl[b]-1;i++) { int x=c-atag[i]; set<int>::iterator it=st[i].lower_bound(x); if(it==st[i].begin())continue; --it; ans=max(ans,*it+atag[i]); } return ans; } int main() { n=read();blo=1000; for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++) { bl[i]=(i-1)/blo+1; st[bl[i]].insert(v[i]); } for(int i=1;i<=n;i++) { int f=read(),a=read(),b=read(),c=read(); if(f==0)add(a,b,c); if(f==1)printf("%d\n",query(a,b,c)); } return 0; } |
分块入门4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define mod 998244353 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,blo; int bl[50005]; ll v[50005],atag[50005],sum[50005]; void add(int a,int b,int c) { for(int i=a;i<=min(bl[a]*blo,b);i++) v[i]+=c,sum[bl[a]]+=c;; if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) v[i]+=c,sum[bl[b]]+=c; for(int i=bl[a]+1;i<=bl[b]-1;i++) atag[i]+=c; } ll query(int a,int b) { ll ans=0; for(int i=a;i<=min(bl[a]*blo,b);i++) ans+=v[i]+atag[bl[a]]; if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) ans+=v[i]+atag[bl[b]]; for(int i=bl[a]+1;i<=bl[b]-1;i++) ans+=sum[i]+blo*atag[i]; return ans; } int main() { n=read();blo=sqrt(n); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++) { bl[i]=(i-1)/blo+1; sum[bl[i]]+=v[i]; } for(int i=1;i<=n;i++) { int f=read(),a=read(),b=read(),c=read(); if(f==0)add(a,b,c); if(f==1) printf("%d\n",query(a,b)%(c+1)); } return 0; } |
分块入门5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define mod 998244353 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,blo; int bl[50005]; int v[50005],sum[50005],flag[50005]; void solve_sqrt(int x) { if(flag[x])return; flag[x]=1; sum[x]=0; for(int i=(x-1)*blo+1;i<=x*blo;i++) { v[i]=sqrt(v[i]),sum[x]+=v[i]; if(v[i]>1)flag[x]=0; } } void add(int a,int b,int c) { for(int i=a;i<=min(bl[a]*blo,b);i++) { sum[bl[a]]-=v[i]; v[i]=sqrt(v[i]); sum[bl[a]]+=v[i]; } if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) { sum[bl[b]]-=v[i]; v[i]=sqrt(v[i]); sum[bl[b]]+=v[i]; } for(int i=bl[a]+1;i<=bl[b]-1;i++) solve_sqrt(i); } int query(int a,int b) { int ans=0; for(int i=a;i<=min(bl[a]*blo,b);i++) ans+=v[i]; if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) ans+=v[i]; for(int i=bl[a]+1;i<=bl[b]-1;i++) ans+=sum[i]; return ans; } int main() { n=read();blo=sqrt(n); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++) { bl[i]=(i-1)/blo+1; sum[bl[i]]+=v[i]; } for(int i=1;i<=n;i++) { int f=read(),a=read(),b=read(),c=read(); if(f==0)add(a,b,c); if(f==1) printf("%d\n",query(a,b)); } return 0; } |
分块入门6:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define mod 998244353 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,blo,m; int v[100005]; vector<int>ve[1005]; int st[200005],top; pair<int,int> query(int b) { int x=1; while(b>ve[x].size()) b-=ve[x].size(),x++; return make_pair(x,b-1); } void rebuild() { top=0; for(int i=1;i<=m;i++) { for(vector<int>::iterator j=ve[i].begin();j!=ve[i].end();j++) st[++top]=*j; ve[i].clear(); } int blo2=sqrt(top); for(int i=1;i<=top;i++) ve[(i-1)/blo2+1].push_back(st[i]); m=(top-1)/blo2+1; } void insert(int a,int b) { pair<int,int> t=query(a); ve[t.first].insert(ve[t.first].begin()+t.second,b); if(ve[t.first].size()>20*blo) rebuild(); } int main() { n=read();blo=sqrt(n); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++) ve[(i-1)/blo+1].push_back(v[i]); m=(n-1)/blo+1; for(int i=1;i<=n;i++) { int f=read(),a=read(),b=read(),c=read(); if(f==0)insert(a,b); if(f==1) { pair<int,int> t=query(b); printf("%d\n",ve[t.first][t.second]); } } return 0; } |
分块入门7:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define mod 10007 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,blo; int v[100005],bl[100005],atag[1005],mtag[1005]; void reset(int x) { for(int i=(x-1)*blo+1;i<=min(n,x*blo);i++) v[i]=(v[i]*mtag[x]+atag[x])%mod; atag[x]=0;mtag[x]=1; } void solve(int f,int a,int b,int c) { reset(bl[a]); for(int i=a;i<=min(bl[a]*blo,b);i++) { if(f==0)v[i]+=c; else v[i]*=c; v[i]%=mod; } if(bl[a]!=bl[b]) { reset(bl[b]); for(int i=(bl[b]-1)*blo+1;i<=b;i++) { if(f==0)v[i]+=c; else v[i]*=c; v[i]%=mod; } } for(int i=bl[a]+1;i<=bl[b]-1;i++) { if(f==0)atag[i]=(atag[i]+c)%mod; else { atag[i]=(atag[i]*c)%mod; mtag[i]=(mtag[i]*c)%mod; } } } int main() { n=read();blo=sqrt(n); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++)bl[i]=(i-1)/blo+1; for(int i=1;i<=bl[n];i++)mtag[i]=1; for(int i=1;i<=n;i++) { int f=read(),a=read(),b=read(),c=read(); if(f==2)printf("%d\n",(v[b]*mtag[bl[b]]+atag[bl[b]])%mod); else solve(f,a,b,c); } return 0; } |
分块入门8:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define mod 998244353 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,blo; int v[100005],bl[100005],tag[100005]; void reset(int x) { if(tag[x]==-1)return; for(int i=(x-1)*blo+1;i<=blo*x;i++) v[i]=tag[x]; tag[x]=-1; } int solve(int a,int b,int c) { int ans=0; reset(bl[a]); for(int i=a;i<=min(bl[a]*blo,b);i++) if(v[i]!=c)v[i]=c; else ans++; if(bl[a]!=bl[b]) { reset(bl[b]); for(int i=(bl[b]-1)*blo+1;i<=b;i++) if(v[i]!=c)v[i]=c; else ans++; } for(int i=bl[a]+1;i<=bl[b]-1;i++) if(tag[i]!=-1) { if(tag[i]!=c)tag[i]=c; else ans+=blo; } else { for(int j=(i-1)*blo+1;j<=i*blo;j++) if(v[j]!=c)v[j]=c; else ans++; tag[i]=c; } return ans; } int main() { memset(tag,-1,sizeof(tag)); n=read();blo=sqrt(n); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++)bl[i]=(i-1)/blo+1; for(int i=1;i<=n;i++) { int a=read(),b=read(),c=read(); printf("%d\n",solve(a,b,c)); } return 0; } |
分块入门9:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define mod 10007 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,blo,id; int v[50005],bl[50005]; int f[505][505]; map<int,int>mp; int val[50005],cnt[50005]; vector<int>ve[50005]; void pre(int x) { memset(cnt,0,sizeof(cnt)); int mx=0,ans=0; for(int i=(x-1)*blo+1;i<=n;i++) { cnt[v[i]]++; int t=bl[i]; if(cnt[v[i]]>mx||(cnt[v[i]]==mx&&val[v[i]]<val[ans])) ans=v[i],mx=cnt[v[i]]; f[x][t]=ans; } } int query(int l,int r,int x) { int t=upper_bound(ve[x].begin(),ve[x].end(),r)-lower_bound(ve[x].begin(),ve[x].end(),l); return t; } int query(int a,int b) { int ans,mx; ans=f[bl[a]+1][bl[b]-1]; mx=query(a,b,ans); for(int i=a;i<=min(bl[a]*blo,b);i++) { int t=query(a,b,v[i]); if(t>mx||(t==mx&&val[v[i]]<val[ans]))ans=v[i],mx=t; } if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) { int t=query(a,b,v[i]); if(t>mx||(t==mx&&val[v[i]]<val[ans]))ans=v[i],mx=t; } return ans; } int main() { n=read(); blo=200; for(int i=1;i<=n;i++) { v[i]=read(); if(!mp[v[i]]) { mp[v[i]]=++id; val[id]=v[i]; } v[i]=mp[v[i]]; ve[v[i]].push_back(i); } for(int i=1;i<=n;i++)bl[i]=(i-1)/blo+1; for(int i=1;i<=bl[n];i++)pre(i); for(int i=1;i<=n;i++) { int a=read(),b=read(); if(a>b)swap(a,b); printf("%d\n",val[query(a,b)]); } return 0; } |
[…] 题讲解 […]
[…] 以hzwer神仙的分块九练为例. […]
[…] 我讲的很差对吧,这应该只是作为我的应该记录吧,如果真的要学习分块,去看hzwer大佬的分块九讲吧,非常清晰。 […]
[…] 因为上方的所有操作都是基于这九道题目给出的,所以不在进行讲解只是放出代码,也可以去看看这位的博客http://hzwer.com/8053.html […]
[…] 这样做的单次均摊复杂度是 (Theta(sqrt n)) 的,具体的证明援引自 命题人 hzwer 大佬的题解: […]
[…] 最后,如果想看分块的板子和拓展,hzwer大佬博客里有,可以去看一看(巨佬的码风是真好看啊)! […]
[…] 分块「分块」数列分块入门1 – 9 by hzwer […]
[…] hzwer的分块九题:http://hzwer.com/8053.html […]
[…] hzwer入门8题:传送门 […]
[…] yhzq 和 黄学长 […]
Amazon Relational Database Service (RDS)
这个03真的没问题吗..应该维护mulitset,同时这个题可能会出现负数吧..直接用-1去max是不是会出锅qwq..
orz hzwer大佬
全班正在抄代码······
真厉害!膜拜膜拜黄大神
大佬Orz%%%
[…] 复习一下「分块」数列分块入门1 – 9 by hzwer […]
[…] 写在前面:这篇博客是对于hzwer学长博客的详解。原链接:http://hzwer.com/8053.html […]
入门三,把set换成vector可提升2500ms左右,但是不加您的快读,恐怕都要超时….
可以直接用数组,更快
黄学长,第三个找前驱的用set分块没问题吗?这样有些重复的数字不会丢掉了吗?
大爱hzwer出教程!
好喜欢黄学长的代码风格
学长 比赛挂了QAQ
http://www.contesthunter.org。这个网址是内网吗?怎么无法注册?
注册确认的时候把网址改成IP
这个网站证书过期了注册不了。。
现在这个网站的域名已经被一些奇奇怪怪的人抢注了…… 这原来是个什么网站啊
[…] 【分块】数列分块入门1-9 by hzwer […]
%%%
%%%
%%%
%%%
%%%黄学长