#6278. 数列分块入门 2
题目链接:https://loj.ac/problem/6278
题目描述
给出一个长为 nn 的数列,以及 nn 个操作,操作涉及区间加法,询问区间内小于某个值 xx 的元素个数。
输入格式
第一行输入一个数字 nn。
第二行输入 nn 个数字,第 ii 个数字为 a_iai,以空格隔开。
接下来输入 nn 行询问,每行输入四个数字 \mathrm{opt}opt、ll、rr、cc,以空格隔开。
若 \mathrm{opt} = 0opt=0,表示将位于 [l, r][l,r] 的之间的数字都加 cc。
若 \mathrm{opt} = 1opt=1,表示询问 [l, r][l,r] 中,小于 c^2c2 的数字的个数。
输出格式
对于每次询问,输出一行一个数字表示答案。
样例
样例输入
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\%100% 的数据,1 \leq n \leq 50000, -2^{31} \leq \mathrm{others}1≤n≤50000,−231≤others、\mathrm{ans} \leq 2^{31}-1ans≤231−1。
个人思路:二分查找区间里面的有多少个符合情况的,时间复杂度差不多是n√nlog√n
#include<iostream> #include<math.h> #include<vector> #include<string.h> #include<algorithm> using namespace std; const int maxn=50000+5; int a[maxn]; int bl[maxn]; int atag[maxn];//存储每个块中所有的元素都要进行的操作 int n; int block;//块的大小 vector<int>v[maxn];//存储每个块里面的元素 void reset(int x) { v[x].clear(); for(int i=(x-1)*block+1;i<=x*block;i++) v[x].push_back(a[i]); sort(v[x].begin(),v[x].end()); } void add(int l,int r,int c) { for(int i=l;i<=min(r,bl[l]*block);i++)//最左边的块 a[i]+=c; reset(bl[l]);//a[i]的值改变了 容器里面也要改变 if(bl[l]!=bl[r])//等于的话岂不是算的重复了 { for(int i=(bl[r]-1)*block+1;i<=r;i++)//最右边的块 a[i]+=c; reset(bl[r]); } for(int i=bl[l]+1;i<=bl[r]-1;i++)//中间的块 atag[i]+=c; } void query(int l,int r,int c) { int ans=0; for(int i=l;i<=min(bl[l]*block,r);i++) { if(a[i]+atag[bl[i]]<c) ans++; } if(bl[l]!=bl[r]) { for(int i=(bl[r]-1)*block+1;i<=r;i++) { if(a[i]+atag[bl[i]]<c) ans++; } } for(int i=bl[l]+1;i<=bl[r]-1;i++) { int x=lower_bound(v[i].begin(),v[i].end(),c-atag[i])-v[i].begin();//二分查找有多少个小于c*c的 ans+=x; } cout<<ans<<endl; } int main() { int opt,l,r,c; memset(atag,0,sizeof(atag)); cin>>n; for(int i=1;i<=n;i++) cin>>a[i];//注意下标从1开始 这样更好分块 block=sqrt(n); for(int i=1;i<=n;i++) { bl[i]=(i-1)/block+1;//a[i]属于哪个块 v[bl[i]].push_back(a[i]); } for(int i=1;i<=bl[n];i++) sort(v[i].begin(),v[i].end()); for(int i=1;i<=n;i++) { cin>>opt>>l>>r>>c; if(opt==0) add(l,r,c); else query(l,r,c*c); } }
当初的梦想实现了吗,事到如今只好放弃吗~