咳咳咳。因为发现最近许多区间题目都可以用莫队来水分,然而我太弱,学习莫队时总是一脸蒙圈,发现莫队里有分块的东东,就又来复习分块了。
切入正题
上一次学习了如何区间修改与单点查询,这次是跨越好大。。。
题目记录
给一个长度为n的序列,操作涉及到区间加法,和求某区间内数字大小不超过x的数的个数。
数据范围:
n<=10000;操作完后所有a[i]<=2^31-1;
粗略一看线段树之类的应该解决不了吧?(。・・)ノ(大犇别打我)
然而分块却可以(≧▽≦)/!
分块解法
还是老样子,按
n√
分一个块。
首先想想,当没有区间加时,该怎么做?
搜索每一个算(滑稽)
很明显是可以通过二分来找,所以我们需要对每一个块排序,然后对于完整的块直接二分查找,不完整的块就暴力。
时间
O(nlogn√)
(排序)+
O(n√logn√)
(查找)。
这里可以让分的块大小不同而提高效率,就不说了。
至于区间加法,对于完整的块直接打下标记;不完整的块就直接暴力,但是这样会影响到块内的单调性,所以需要再排序一次。时间复杂度不计。
那么这时查询时,不再是对小于x查询,而是对小于x-lab[i]进行查询,lab[i]表示第i块的标记。
代码
打的好丑啊(。・・)ノ
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1e4+5;
int bl[maxn],n,block,lab[103],m;
struct cy{
int val,id; //id表示当前数的位置。
}a[maxn];
bool cmp(cy a,cy b)
{
return a.val<b.val;
}
void add(int l,int r,int x)//区间加。
{
bool bz1,bz2;
if (bl[l]==bl[l-1]) bz1=false; else bz1=true;
if (bl[r]==bl[r+1]) bz2=false; else bz2=true;
if (bl[l]==bl[r]){
if (bz1&&bz2) lab[bl[l]]+=x;
else {
fo(i,(bl[l]-1*block+1),bl[r]*block)//范围不是l~r,而是这个块!下同。
if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;//判断位置在不在区间内,因为排序改变了每块内的每个数的位置。
sort(a+block*(bl[l]-1)+1,a+block*(bl[r]),cmp);
}
return;
}
if (bz1&&bz2)
fo(i,bl[l],bl[r])
lab[i]+=x;
else if (!bz1&&!bz2){
fo(i,bl[l]+1,bl[r]-1)
lab[i]+=x;
fo(i,(bl[l]-1)*block+1,bl[l]*block)
if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;
sort(a+(bl[l]-1)*block+1,a+bl[l]*block+1,cmp);
fo(i,(bl[r]-1)*block+1,bl[r]*block)
if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;
sort(a+(bl[r]-1)*block+1,a+bl[r]*block+1,cmp);
}
else if (bz1&&!bz2){
fo(i,bl[l],bl[r]-1)
lab[i]+=x;
fo(i,(bl[r]-1)*block,bl[r]*block)
if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;
sort(a+(bl[r]-1)*block+1,a+bl[r]*block+1,cmp);
}
else {
fo(i,bl[l]+1,bl[r])
lab[i]+=x;
fo(i,(bl[l]-1)*block+1,bl[l]*block)
if (a[i].id>=l&&a[i].id<=r) a[i].val+=x;
sort(a+(bl[l]-1)*block+1,a+bl[l]*block+1,cmp);
}
}
int mefind(int l,int r,int x) //块内二分查找。
{
int now=l;
while (l<=r){
int mid=(l+r)/2;
if (a[mid].val<x-lab[bl[mid]]) l=mid+1;
else r=mid-1;
}
return l-now;
}
int find(int l,int r,int x)//查询。
{
int sum=0;
bool bz1,bz2;
if (bl[l]==bl[l-1]) bz1=false; else bz1=true;
if (bl[r]==bl[r+1]) bz2=false; else bz2=true;
if (bl[l]==bl[r]) {
if (bz1&&bz2) sum=mefind(l,r,x);
else {
fo(i,(bl[l]-1)*block+1,bl[r]*block)
if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
}
return sum;
}
if (bz1&&bz2)
fo(i,bl[l],bl[r])
sum+=mefind((i-1)*block+1,i*block,x);
else if (!bz1&&!bz2) {
fo(i,bl[l]+1,bl[r]-1) sum+=mefind((i-1)*block+1,i*block,x);
fo(i,(bl[l]-1)*block+1,bl[l]*block)
if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
fo(i,(bl[r]-1)*block+1,bl[r]*block)
if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
}
else if (bz1&&!bz2){
fo(i,bl[l],bl[r]-1) sum+=mefind((i-1)*block+1,i*block,x);
fo(i,(bl[r]-1)*block+1,bl[r]*block)
if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
}
else {
fo(i,bl[l]+1,bl[r]) sum+=mefind((i-1)*block+1,i*block,x);
fo(i,(bl[l]-1)*block+1,bl[l]*block)
if (a[i].val+lab[bl[i]]<x&&a[i].id>=l&&a[i].id<=r) ++sum;
}
return sum;
}
int main()
{
freopen("T.in","r",stdin);
scanf("%d%d",&n,&m);
block=sqrt(n);
fo(i,1,n){
scanf("%d",&a[i].val);
a[i].id=i;
bl[i]=(i-1)/block+1;
}
bl[0]=1; bl[n+1]=bl[n];
fo(i,1,block) sort(a+block*(i-1)+1,a+block*i+1,cmp);
fo(i,1,m){
int t,l,r,x;
scanf("%d%d%d%d",&t,&l,&r,&x);
if (!t) add(l,r,x);
else printf("%d\n",find(l,r,x));
}
}
打了好久,虽然拍对了,但没交过题,好虚。。。。