数列分块入门——loj

数列分块入门1

传送门
数列分块 就是把一个数组分成一块一块的每个块的大小为sqrt(n) 对于每次对元素的查询修改 可以直接对它所在的块操作 可以节省很多时间

//乱七八糟的维护区间的 用分块去做
//分了根号n块 复杂度为根号n 
#include<bits/stdc++.h> 
using namespace std;
const int maxn=1e5+10;
long long belong[maxn],block,l[maxn],r[maxn],num=0;
long long n;
long long a[maxn];
long long add[maxn];
void build()
{
	block=sqrt(n);//块的大小 
	num=n/block;if(n%block)num++;//块的个数 
	for(int i=1;i<=num;i++)
	{
		l[i]=(i-1)*block+1;
		r[i]=i*block;//每一个块的左右边界 
	}
	r[num]=n;
	for(int i=1;i<=n;i++)
		belong[i]=(i-1)/block+1;//每个元素属于哪个块 
} 
void update(long long x,long long y,long long z)
{
	if(belong[x]==belong[y])
	{
		for(int i=x;i<=y;i++)
		a[i]+=z;
	}
	else
	{
		for(int i=belong[x]+1;i<=belong[y]-1;i++)//对包括在x到y之间的整数块进行操作  
		add[i]+=z;
		for(int i=x;i<=r[belong[x]];i++)如果x y之间有不是在整数块内的  对其单独操作即可
		a[i]+=z;
		for(int i=l[belong[y]];i<=y;i++)
		a[i]+=z;
	}
}
//long long ask(int x,int y)
//{
//	long long ans=0;
//	if(belong[x]==belong[y])
//	{
//		for(int i=x;i<=y;i++)
//		ans=max(ans,a[i]);
//		return ans;
//	}
//	for(int i=x;i<=r[belong[x]];i++)
//	{
//		ans=max(ans,a[i]);
//	}
//	for(int i=belong[x]+1;i<belong[y];i++)
//	ans=max(ans,Max[i]);
//	for(int i=l[belong[y]];i<=y;i++)
//	ans=max(ans,a[i]);
//	return ans;
//}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	build();
	for(int i=1;i<=n;i++)
	{
		int op,l,r,w;
		cin>>op>>l>>r>>w;
		if(op==0)
		update(l,r,w);
		else
		cout<<a[r]+add[belong[r]]<<endl;//add标记的是对块操作的值
	}
}

数列分块入门2

传送门
这次问的不是某个点的值了,题目问的是l r 区间有多少个元素小于等于cc
看了dalao的写法 将每个块内的元素用vector存起来 并且从小到大排序 如果有更新到某个块内的元素 我们就将这个块所在的vector重新存 再排序 求解每个整块内有几个小于c
c的时候我们之间用lower_bound即可

//我看过你说想要时间停止 拉着我的手喊我名字
//这道题困扰了我好久 有些变量搞错了 做题一定要细心啊!
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
typedef long long ll;
ll a[5*maxn],l[5*maxn],r[5*maxn],belong[5*maxn],block,num;
ll n,tag[5*maxn];
vector<int>v[10001];
void build()
{
	block=sqrt(n);//依然是每个块的大小
	num=n/block;//有几个块
	if(n%block)//多出来的也要存到末尾块里 块树加一
		num++;
	for(int i=1; i<=n; i++)
	{
		belong[i]=(i-1)/block+1;//每个元素属于哪个块
		v[belong[i]].push_back(a[i]);//将属于这个块的元素都存到vector中去
	}
	r[num]=n;//防止越界嘛
	for(int i=1; i<=num; i++)
	{
		l[i]=(i-1)*block;//每个块的边界  这里也困扰到我了  实际上 r[i-1]=l[i] 在计算的时候 不能重复计算进去 
		r[i]=i*block;
		sort(v[i].begin(),v[i].end());
	}
}
void update(int x)//这里的x我传进来的是块下标 而不是元素下标
{
		v[x].clear();//当前块内的元素被更新 要对它的vector也更新一下!
		for(int i=l[x]+1;i<=r[x];i++)//将属于当前块的元素都重新放进去 为什么从l[x]+1开始 因为l[x]=r[x-1] 下面的 +1 同理
			v[x].push_back(a[i]);
			sort(v[x].begin(),v[x].end());//再次排序
}
void add(long long x,long long y,long long c)
{
//可以分为三个部分 1、x到x所在块的右边界  2、x右边界到y左边界中间的整块 3、 y的左边界到y 
	for(int i=x;i<=min(r[belong[x]],y);i++) a[i]+=c;
	// 部分1   如果x和y在一个块内 那这个块中并不是所有的元素都被更新了 只有x到y 此时y<=r[belong[x]]  
	//如果x y不在一个块内 我们只需要更新不在整块内的元素即可 在整块的区间直接对块操作 此时y>r[belong[x]]
	update(belong[x]);//x所在的块中元素被更新 其对应vector也要更新
	if(belong[x]!=belong[y])// 部分2  x y不在一个块内的情况 需要对y所在的块处理一下
	{
		for(int i=l[belong[y]]+1;i<=y;i++)
			a[i]+=c;
			update(belong[y]);
	}
	for(int i=belong[x]+1;i<=belong[y]-1;i++)//部分3   x y区间中整块的部分 直接对块操作
	tag[i]+=c;
}
long long getsum(long long x,long long y,long long c)
{//这里跟上面是一样的
	long long ans=0;
	for(int i=x;i<=min(r[belong[x]],y);i++)
	{
		if(a[i]+tag[belong[x]]<c)
		ans++;
	}
	if(belong[x]!=belong[y])
	{
		for(int i=l[belong[y]]+1;i<=y;i++)//
		if(a[i]+tag[belong[y]]<c)
		ans++;
	}
	for(int i=belong[x]+1;i<=belong[y]-1;i++)
	{
		int t=c-tag[i];
		ans+=lower_bound(v[i].begin(),v[i].end(),t)-v[i].begin();
	}
	return ans;
}
int main()
{
	int i,op,l,r,c;
	cin>>n;
	for(i=1; i<=n; i++)
		cin>>a[i];
	build();
	for(i=1; i<=n; i++)
	{
		cin>>op>>l>>r>>c;
		if(op==0)
			add(l,r,c);
		else
		{
			cout<<getsum(l,r,c*c)<<endl;
		}
	}
}
/*
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
*/

牛客
昨天比赛的时候一看这道题就想到了分块 但是还是没写对 刚刚看了过了90%
改了一处过了
这道题问的是l r区间内是不是单调递增的
老样子 哪个块修改了 就去更新这个块 tag标记这个块是不是完美的 不是完美的标记为1 完美的标记为2 另外需要注意的是 块与块之间也要是完美的 只需要判断 两个块衔接的端点处就可以了 看见别人有用c++函数做的 很简单用is_sorted(l,r)如果l r区间内是升序的返回1 否则返回0 还有用线段树啥的做的 都可以吧 但是我仍然选择我新学的分块

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
long long n,block,r[maxn*3],l[maxn*3],num,belong[maxn*3];
long long a[maxn*3],tag[maxn*3];
void build()
{
	for(int i=1; i<=num; i++)
	{
		l[i]=(i-1)*block;
		r[i]=i*block;
		for(int j=l[i]+1; j<=r[i]&&j<=n; j++)
		{
			if(a[j]>=a[j-1])
				continue;
			tag[i]=1;
			break;
		}
	}
	r[num]=n;
}
void update(long long x,long long y)
{
	a[x]=y;
	for(int i=l[belong[x]]+1; i<=r[belong[x]]; i++)
	{
		if(a[i]>=a[i-1])
			continue;
		else
		{
			tag[belong[x]]=1;
			return ;
		}
	}
	tag[belong[x]]=0;
	return ;

}
int query(long long x,long long y)
{
	if(belong[x]==belong[y])
	{
		for(int i=x+1;i<=y;i++)
		{
			if(a[i]>=a[i-1])
			continue;
			else
			return 0;
		}
		return 1;
	}
	for(int i=belong[x]+1; i<=belong[y]-1; i++)
	{
		if(tag[i])
		{
			return 0;
		}
		if(a[r[i]]<=a[r[i]+1])
		continue;
		else
		return 0;
	}
	for(int i=x+1;i<=r[belong[x]]+1;i++)
	{
		if(a[i]>=a[i-1])
		continue;
		else
		return 0;
	}
	for(int i=l[belong[y]]+1;i<=y;i++)
	{
		if(a[i]>=a[i-1])
		continue;
		else
		return 0;
	}
	return 1;
}
int main()
{
	int i,j,m;
	cin>>n>>m;
	block=sqrt(n);
	num=n/block;
	if(n%block)
		num++;
	for(i=1; i<=n; i++)
	{
		cin>>a[i];
		belong[i]=(i-1)/block+1;
	}
	build();
	for(i=1; i<=m; i++)
	{
		long long op,l,r;
		cin>>op>>l>>r;
		if(op==1)
		{
			update(l,r);
		}
		else
		{
            if(l==r||n==1)
            {
            printf("Yes\n");
            continue;
            }
			if(query(l,r))
				printf("Yes\n");
			else
				printf("No\n");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值