分块5

题目链接:https://loj.ac/problem/6281

#6281. 数列分块入门 5

内存限制:256 MiB时间限制:500 ms标准输入输出

题目描述

给出一个长为 nn 的数列 a_1\ldots a_na1​…an​,以及 nn 个操作,操作涉及区间开方,区间求和。

输入格式

第一行输入一个数字 nn。

第二行输入 nn 个数字,第 ii 个数字为 a_iai​,以空格隔开。

接下来输入 nn 行询问,每行输入四个数字 \mathrm{opt}, l, r, copt,l,r,c,以空格隔开。

若 \mathrm{opt} = 0opt=0,表示将位于 [l, r][l,r] 的之间的数字都开方。对于区间中每个 a_i(l\le i\le r),\: a_i ← \left\lfloor \sqrt{a_i}\right\rfloorai​(l≤i≤r),ai​←⌊ai​​⌋

若 \mathrm{opt} = 1opt=1,表示询问位于 [l, r][l,r] 的所有数字的和。

输出格式

对于每次询问,输出一行一个数字表示答案。

样例

样例输入

4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4

样例输出

6
2

思路:

这里有个小思维,因为数的大小小于2^31,最多开方5次就为1。所以我们做法和上面一样有sum数组维护整块的值,

计算整块的时候,用flag记录整组都为1的情况,如果全为1就加block(块的大小),不是就暴力加,不会超时的,因为最多5次开方

就全为1了,时间复杂度和上面的一样(5倍对时间复杂度没关系)

ac代码:

#include<iostream>
#include<cmath> 
#include<string.h>
#include<vector>
#include<set>
using namespace std;
const int maxn=50000+5;
typedef long long ll;
ll v[50000+5],pos[50000+5],flag[50000];//pos保存分块情况,flag存储块更新情况 
ll n,m;
ll s[50000];//维护各块的和 
void update(int l,int r){//更新 
	for(int i=l;i<=min(pos[l]*m,(ll)r);i++){//左边不完整块,暴力加 
		s[pos[l]]-=v[i];
		v[i]=sqrt(v[i]);
		s[pos[l]]+=v[i];
	}
	if(pos[l]!=pos[r]){//右边不完整块,暴力加
		for(int i=(pos[r]-1)*m+1;i<=r;i++){
			s[pos[r]]-=v[i];
			v[i]=sqrt(v[i]);
			s[pos[r]]+=v[i];
		}
	}
	for(int i=pos[l]+1;i<=pos[r]-1;i++){
		if(flag[i]) continue;
		else{
			flag[i]=1;//先假设全为1,后面如果不是改为0
			for(int j=(i-1)*m+1;j<=i*m;j++){
				s[i]-=v[j];
				v[j]=sqrt(v[j]);
				s[i]+=v[j];
				if(v[j]!=1)
				flag[i]=0;
			} 
		}
	}
}
void query(int l,int r){
	ll ans=0;
	for(int i=l;i<=min(pos[l]*m,(ll)r);i++){//左边不完整快,暴力加 
		ans+=v[i];
	}
	if(pos[l]!=pos[r]){//右边不完整块,暴力加 
		for(int i=(pos[r]-1)*m+1;i<=r;i++){
			ans+=v[i];
		}
	}
	for(int i=pos[l]+1;i<=pos[r]-1;i++){
		ans+=s[i];
	}
	cout<<ans<<endl;
}
int main(){
	cin>>n;
	m=sqrt(n);//分成m块 
	memset(flag,0,sizeof(flag));
	for(int i=1;i<=n;i++){
		cin>>v[i];
	}
	for(int i=1;i<=n;i++){
		pos[i]=(i-1)/m+1;//分块操作
		s[pos[i]]+=v[i];//计算每一块的数值和 
	}
	int opt,l,r,c;
	for(int i=1;i<=n;i++){
		cin>>opt>>l>>r>>c;
		if(opt==0){
			update(l,r);
		}
		else{
			query(l,r);
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值