题目描述
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。
输入格式
第一行输入一个数字 n。
第二行输入 n 个数字,第 i 个数字为 ai,以空格隔开。
接下来输入 n 行询问,每行输入四个数字 opt、l、r、c,以空格隔开。
若 opt=0,表示将位于 [l, r] 的之间的数字都加 c。
若 opt=1,表示询问 [l, r] 中,小于 c^2 的数字的个数。
输出格式
对于每次询问,输出一行一个数字表示答案。
样例
样例输入
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
样例输出
3
0
2
数据范围与提示
对于100% 的数据,1≤n≤50000,−2^31≤others、ans≤2^31−1
方法一
用vector+sort维护每个块的有序性
不完整块暴力修改后要重新排序
在LOJ上A了,然而在入门OJ上TLE了。。。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cmath> 5 #include<vector> 6 using namespace std; 7 const int N=5e4+5; 8 int n,len,v[N],bl[N],tag[N]; 9 vector<int> d[230]; 10 int read() 11 { 12 int f=1,x=0; 13 char ch=getchar(); 14 while(!isdigit(ch)) 15 { 16 if(ch=='-')f=-1; 17 ch=getchar(); 18 } 19 while(isdigit(ch)) 20 { 21 x=x*10+ch-'0'; 22 ch=getchar(); 23 } 24 return x*f; 25 } 26 void up(int x) 27 { 28 d[x].clear(); 29 for(int i=(x-1)*len+1; i<=min(n,x*len); i++) 30 d[x].push_back(v[i]); 31 sort(d[x].begin(),d[x].end()); 32 } 33 void add(int a,int b,int c) 34 { 35 for(int i=a; i<=min(b,bl[a]*len); i++) 36 v[i]+=c; 37 up(bl[a]); 38 if(bl[a]!=bl[b]) 39 { 40 for(int i=(bl[b]-1)*len+1; i<=b; i++) 41 v[i]+=c; 42 up(bl[b]); 43 } 44 for(int i=bl[a]+1; i<=bl[b]-1; i++) 45 tag[i]+=c; 46 } 47 int query(int a,int b,int c) 48 { 49 int ans=0; 50 for(int i=a; i<=min(b,bl[a]*len); i++) 51 if(v[i]+tag[bl[i]]<c)++ans; 52 if(bl[a]!=bl[b]) 53 for(int i=(bl[b]-1)*len+1; i<=b; i++) 54 if(v[i]+tag[bl[i]]<c)++ans; 55 for(int i=bl[a]+1; i<=bl[b]-1; i++) 56 { 57 int x=c-tag[i]; 58 ans+=lower_bound(d[i].begin(),d[i].end(),x)-d[i].begin(); 59 } 60 return ans; 61 } 62 int main() 63 { 64 n=read(); 65 len=sqrt(n); 66 for(int i=1; i<=n; i++) 67 { 68 v[i]=read(); 69 bl[i]=(i-1)/len+1; 70 d[bl[i]].push_back(v[i]); 71 } 72 for(int i=1; i<=bl[n]; i++) 73 sort(d[i].begin(),d[i].end()); 74 for(int i=1; i<=n; i++) 75 { 76 int f=read(),a=read(),b=read(),c=read(); 77 if(f==0)add(a,b,c); 78 else printf("%d\n",query(a,b,c*c)); 79 } 80 return 0; 81 }
方法二
对每个块维护块内最值mx和mn
统计完整块时,如果c>mx[i],ans+=len,如果c<=mn[i]就直接跳过
否则暴力统计
这个方法在入门OJ上A了
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 const int N=50010; 8 int n,len; 9 int v[N],bl[N],mx[300],mn[300],tag[300]; 10 int read() 11 { 12 int x=0,f=1; 13 char ch=getchar(); 14 while(!isdigit(ch)) 15 { 16 if(ch=='-')f=-1; 17 ch=getchar(); 18 } 19 while(isdigit(ch)) 20 { 21 x=x*10+ch-'0'; 22 ch=getchar(); 23 } 24 return x*f; 25 } 26 void add(int a,int b,int c) 27 { 28 for(int i=a; i<=min(b,bl[a]*len); i++) 29 { 30 v[i]+=c; 31 mx[bl[i]]=max(mx[bl[i]],v[i]+tag[bl[i]]); 32 mn[bl[i]]=min(mn[bl[i]],v[i]+tag[bl[i]]); 33 } 34 if(bl[a]!=bl[b]) 35 for(int i=(bl[b]-1)*len+1; i<=b; i++) 36 { 37 v[i]+=c; 38 mx[bl[i]]=max(mx[bl[i]],v[i]+tag[bl[i]]); 39 mn[bl[i]]=min(mn[bl[i]],v[i]+tag[bl[i]]); 40 } 41 for(int i=bl[a]+1; i<=bl[b]-1; i++) 42 tag[i]+=c,mx[i]+=c,mn[i]+=c; 43 } 44 int query(int a,int b,int c) 45 { 46 int ans=0; 47 for(int i=a; i<=min(b,bl[a]*len); i++) 48 if(v[i]+tag[bl[a]]<c)++ans; 49 if(bl[a]!=bl[b]) 50 for(int i=(bl[b]-1)*len+1; i<=b; i++) 51 if(v[i]+tag[bl[b]]<c)++ans; 52 for(int i=bl[a]+1; i<=bl[b]-1; i++) 53 { 54 if(c<=mn[i])continue; 55 if(c>mx[i])ans+=len; 56 else 57 { 58 for(int j=(i-1)*len+1; j<=i*len; j++) 59 if(v[j]+tag[i]<c)++ans; 60 } 61 } 62 return ans; 63 } 64 int main() 65 { 66 n=read(); 67 len=sqrt(n); 68 memset(mn,0x3f,sizeof(mn)); 69 for(int i=1; i<=n; i++) 70 { 71 v[i]=read(); 72 bl[i]=(i-1)/len+1; 73 mx[bl[i]]=max(mx[bl[i]],v[i]); 74 mn[bl[i]]=min(mn[bl[i]],v[i]); 75 } 76 for(int i=1; i<=n; i++) 77 { 78 int f=read(),a=read(),b=read(),c=read(); 79 if(!f)add(a,b,c); 80 else printf("%d\n",query(a,b,c*c)); 81 } 82 return 0; 83 }