分块1-----#6277. 数列分块入门 1

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

题目描述

给出一个长为 nn 的数列,以及 nn 个操作,操作涉及区间加法,单点查值。

输入格式

第一行输入一个数字 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,表示询问 a_rar​ 的值(ll 和 cc 忽略)。

输出格式

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

样例

样例输入

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

样例输出

2
5

数据范围与提示

对于 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。

 

首先看到这题,第一时间就想到直接模拟,就是下面一段代码:

#include<iostream>
using namespace std;
typedef long long ll;
int n,opt,l,r,c;
ll a[50005];
int main(){
	while(cin>>n){
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		while(n--){
			cin>>opt>>l>>r>>c;
			if(opt==0){
				for(int i=l;i<=r;i++){
					a[i]+=c;
				}
			}
			else if(opt==1){
				cout<<a[r]<<endl;
			}
		}
			
	}
	return 0;
} 

很明显,当数据足够大时,会超时。这是就需要用到分块的思想了:

1.一般以根号n为一个块(区间)的长度。

2.分到最后如果有不够的,就单独成一个小块

3.举个例子:

n=12 N=sqrt(12) 向下取整 3 
第一块:1~3 第二块:4~6 第三块:7~9 第四块: 10~12 
属于第几块pos[i]=(i-1)/N+1; 
如果是完整的块就直接更新到标记里 
不完整的块就直接暴力更新就ok

以下是这题的ac代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int m,pos[51000];
int s[51000],flag[51000];
void change(int a,int b,int c)
{
    for (int i=a;i<=min(b,pos[a]*m);i++) s[i]+=c;//如果b在a的块内就更新到b 否则更新到a的块的结尾
    if (pos[a]!=pos[b])
    {
        for (int i=(pos[b]-1)*m+1;i<=b;i++) s[i]+=c; //如果ab不同块 更新b的块内的开头到b 
    } 
    for (int i=pos[a]+1;i<=pos[b]-1;i++) flag[i]+=c;//ab中间的块标记都更新
}
int main()
{
    int n;
    scanf("%d",&n);
    m=sqrt(n);
    for (int i=1;i<=n;i++) pos[i]=(i-1)/m+1;
    for (int i=1;i<=n;i++) scanf("%d",&s[i]);
    for (int i=1;i<=n;i++)
    {
        int x,a,b,c;
        scanf("%d%d%d%d",&x,&a,&b,&c);
        if (x==0)
        {
            change(a,b,c);
        }
        else if (x==1) 
        {
            printf("%d\n",flag[pos[b]]+s[b]);//b所在的块标记加上b点的值
        }
    }
    return 0;
}

参考博客链接:https://blog.csdn.net/qq_36038511/article/details/79725027

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值