I - POJ 3468 分块

本文介绍了一种处理大规模数组中区间加法与求和查询的高效算法,通过将数组分块并维护每块的和与增量,实现了快速更新与查询。适用于数据结构与算法竞赛及高性能计算场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of AaAa+1, ... , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.


题意: 输入N,Q 。 N表示有几个数,以下Q行表示有几个操作, 如果输入为C, a, b, c 代表从第a个数开始到第b个数结束,每个数都加c;Q, a, b表示查询从第a个数到第b个数区间内的和并输出。


#include<iostream>
#include<stack>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<cmath>
#include<math.h>
using namespace std;
# define ll long long
const int maxn = 1e6+100;
int n, m;
ll mapp[maxn],sum[maxn],add[maxn];
int l[maxn],r[maxn];
int belong[maxn];

//常规操作;
void build()
{
    int block = (int)sqrt(n*1.0);//每块的大小;
    //块数;
    int num=n/block;
    if(n%block)
        num++;

    //每个数属于哪一块;
    for(int i=1; i<=n; i++)
    {
        belong[i]=(i-1)/block+1;
    }
    //该数所在块的左边界和右边界;
    for(int i=1; i<=num; i++)
    {
        l[i]=(i-1)*block+1;
        r[i]=i*block;
    }
    r[num]=n;//最后一个数的右边界一定为n;
    //第i块内包含数的和为 sum[i],(计算每一块的和);
    for(int i=1; i<=num; i++)
    {
        for(int j=l[i];j<=r[i];j++)
        {
            sum[i]+=mapp[j];
        }
    }
}
//区间的更新;
void update(int x, int y, ll val)
{
    //如果给定的两个点属于同一块(特殊情况),那么就直接在其点上+val,区间和+(y-x+1)val;
    if(belong[x]==belong[y])
    {
        for(int i=x; i<=y; i++)
        {
            mapp[i]+=val;
            sum[belong[x]]+=val;
        }
        return ;
    }
    //对于x所在块,从x开始到x所在块右边界结束,进行同上操作;
    for(int i=x; i<=r[belong[x]]; i++)
    {
        mapp[i]+=val;
        sum[belong[x]]+=val;
    }
    //对于y所在块,从y所在块的左边界开始到y结束,进行同上上操作;
    for(int i=l[belong[y]]; i<=y; i++)
    {
        mapp[i]+=val;
        sum[belong[y]]+=val;
    }
    //对于除去x所在块和y所在块之外,中间的每一块;
    for(int i=belong[x]+1; i<belong[y]; i++)
    {
        add[i]+=val;
    }
}
//访问区间[x, y];
ll ask(int x,int y)
{
    ll ans=0;
    //如果所查询区间刚好在一个块里(特殊情况);
    if(belong[x]==belong[y])
    {
        for(int i=x; i<=y; i++)
        {
            ans+=mapp[i]+add[belong[x]];
        }
        return ans;
    }
    //在循环x所在块内的数字时,每次要加上add[belong[x]];
    for(int i=x; i<=r[belong[x]];i++)
    {
        ans+=mapp[i]+add[belong[x]];
    }
    //在循环y所在块内的数字时,每次要加上add[belong[x]];
    for(int i=l[belong[y]]; i<=y; i++)
    {
        ans+=mapp[i]+add[belong[y]];
    }
    //除了x和y所在块,循环每一块时,都要加上每一块的包含的数字个数*add[i]即:+add[i]*(r[i]-l[i]+1)
    for(int i=belong[x]+1;i<belong[y];i++)
    {
        ans+=sum[i]+add[i]*(r[i]-l[i]+1);
    }
    return ans;
}
int main()
{
    scanf("%d %d",&n, &m);
    for(int i=1; i<=n; i++)
    {
        scanf("%lld", &mapp[i]);
    }
    build();
    char str[10];
    int x, y;
    ll cost;
    while(m--)
    {
        scanf("%s", str);
        if(str[0]=='Q')
        {
            scanf("%d %d",&x,&y);
            printf("%lld\n",ask(x, y));
        }
        else if(str[0]=='C')
        {
            scanf("%d %d %lld",&x,&y,&cost);
            update(x, y, cost);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值