[洛谷P2801]教主的魔法
Posted Mrsrz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[洛谷P2801]教主的魔法相关的知识,希望对你有一定的参考价值。
题目大意:有n个数,q个操作。操作有两种:①把一段区间所有数加上p;②查询一段区间内大于等于p的元素的个数。
解题思路:询问次数少,可以用分块解决。
将所有数分成$\sqrt{n}$块,对每一块进行排序。
用a数组存原来的数列,b数组存排完序后的数列。
查询时,对左右两个块中的数据暴力查询,中间的由于整块访问并排完序,直接二分查找即可。
修改数据时,对左右两个块中的数据暴力修改,并且更新b数组(重新排序),对中间的,由于相对大小不变,直接打上标记即可。
修改时,最多对两个块进行排序,时间复杂度$O(\sqrt{n}\log_2\sqrt{n})$。
查询时,最多对所有块都进行二分查找,时间复杂度$O(\sqrt{n}\log_2\sqrt{n})$。
故总时间复杂度$O(q\sqrt{n}\log_2\sqrt{n})$。
C++ Code:
#include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<cctype> using namespace std; int a[1000005],b[1000005]; int n,q,size,l[1005],r[1005],blocks,add[1005],inbk[1000005]; char s[4]; inline int readint(){ char c=getchar(); for(;!isdigit(c);c=getchar()); int d=0; for(;isdigit(c);c=getchar()) d=(d<<3)+(d<<1)+(c^‘0‘); return d; } void chan(int blk){ for(int i=l[blk];i<=r[blk];++i) b[i]=a[i]; sort(b+l[blk],b+r[blk]+1); } int main(){ n=readint();q=readint(); size=(int)(sqrt(n)+0.00000001); blocks=0; l[1]=1; for(int i=1;i<=n;++i){ a[i]=b[i]=readint(); inbk[i]=blocks+1; if(i%size==0){ r[++blocks]=i; l[blocks+1]=i+1; } } if(n%size) r[++blocks]=n; for(int i=1;i<=blocks;++i) sort(b+l[i],b+r[i]+1); memset(add,0,sizeof add); while(q--){ scanf("%s",s); if(s[0]==‘M‘){ int L=readint(),R=readint(),p=readint(); if(L>R)L^=R^=L^=R; int lft=inbk[L],rgt=inbk[R]; if(lft==rgt){ for(int i=L;i<=R;++i)a[i]+=p; chan(lft); }else{ for(int i=lft+1;i<rgt;++i)add[i]+=p; for(int i=L;i<=r[lft];++i)a[i]+=p; for(int i=l[rgt];i<=R;++i)a[i]+=p; chan(lft); chan(rgt); } }else{ int L=readint(),R=readint(),p=readint(),ans=0; int lft=inbk[L],rgt=inbk[R]; if(lft==rgt){ for(int i=L;i<=R;++i) if(a[i]+add[lft]>=p)++ans; }else{ for(int i=lft+1;i<rgt;++i) ans+=r[i]-(lower_bound(b+l[i],b+r[i]+1,p-add[i])-b-1); for(int i=L;i<=r[lft];++i) if(a[i]+add[lft]>=p)++ans; for(int i=l[rgt];i<=R;++i) if(a[i]+add[rgt]>=p)++ans; } printf("%d\n",ans); } } return 0; }
以上是关于[洛谷P2801]教主的魔法的主要内容,如果未能解决你的问题,请参考以下文章