luogu2801教主的魔法(分塊+二分)
阿新 • • 發佈:2018-11-12
洛谷P2801
題目:
教主最近學會了一種神奇的魔法,能夠使人長高。於是他準備演示給XMYZ資訊組每個英雄看。於是N個英雄們又一次聚集在了一起,這次他們排成了一列,被編號為1、2、……、N。
每個人的身高一開始都是不超過1000的正整數。教主的魔法每次可以把閉區間[L, R](1≤L≤R≤N)內的英雄的身高全部加上一個整數W。(雖然L=R時並不符合區間的書寫規範,但我們可以認為是單獨增加第L(R)個英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪,於是他們有時候會問WD閉區間 [L, R] 內有多少英雄身高大於等於C,以驗證教主的魔法是否真的有效。
WD巨懶,於是他把這個回答的任務交給了你。
分析:
增加身高——打標記
快速查詢——排序(快排),二分
注意:
分塊:
——塊數,平方根複雜化
——邊界,min(k*block,n)
二分:
邊界,最後的取值
#include<bits/stdc++.h> #define maxn 1000010 using namespace std; int n,m,block,num; int a[maxn],b[maxn],ls[maxn],rs[maxn],pos[maxn],s[maxn]; void reset(int k){ int l=(k-1)*block+1,r=min(k*block,n); for(int i=l;i<=r;i++) b[i]=a[i]; sort(b+l,b+r+1); ls[k]=l;rs[k]=r; return ; } void work(int x,int y,int z){ int ans=0; if(pos[x]==pos[y]){ for(int i=x;i<=y;i++) if(a[i]+s[pos[x]]>=z)ans++; } else{ for(int i=x;i<=rs[pos[x]];i++)if(a[i]+s[pos[x]]>=z)ans++; for(int i=ls[pos[y]];i<=y;i++)if(a[i]+s[pos[x]]>=z)ans++; for(int i=pos[x]+1;i<=pos[y]-1;i++){ int left=ls[i],right=rs[i],mid; while(left<=right){ mid=(left+right)/2; if(b[mid]<z-s[i])left=mid+1; else right=mid-1; } ans=ans+rs[i]-left+1; } } printf("%d\n",ans); return ; } void work1(int x,int y,int z){ if(pos[x]==pos[y]) for(int i=x;i<=y;i++)a[i]=a[i]+z; else{ for(int i=x;i<=rs[pos[x]];i++)a[i]=a[i]+z; for(int i=ls[pos[y]];i<=y;i++)a[i]=a[i]+z; } reset(pos[x]);reset(pos[y]); for(int i=pos[x]+1;i<=pos[y]-1;i++)s[i]=s[i]+z; return ; } int main(){ memset(s,0,sizeof(s)); scanf("%d %d\n",&n,&m); block=sqrt(n*1.0); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); pos[i]=(i-1)/block+1; b[i]=a[i]; } num=n/block;if(n%num!=0)num++; for(int i=1;i<=num;i++)reset(i); for(int i=1;i<=m;i++){ int x,y,z; char h; scanf("\n%c %d %d %d",&h,&x,&y,&z); if(h=='A')work(x,y,z); if(h=='M')work1(x,y,z); } return 0; }