【XSY2111】Chef and Churus 分块 树状数组


题目描述

  有一个长度为\(n\)的数组\(A\)\(n\)个区间\([l_i,r_i]\),有\(q\)次操作:

   \(1~x~y\):把\(a_x\)改成\(y\)

   \(2~x~y\):求第\(l\)个区间到第\(r\)个区间的区间和的和。

  \(n,q\leq {10}^5,a_i\leq {10}^9\)

题解

  分块。

  设\(s_i\)为第\(i\)块的所有区间的区间和,\(d_{i,j}\)为第\(i\)块有多少个区间包含了\(j\)这个位置。

  修改时修改树状数组和每个区间的区间和。设当前\(a_x=v\),则\(s_i+=(y-v)\times d_{i,x}\)

  查询时完整的区间直接查询区间和,不完整的区间就暴力查询。

  设块大小为\(m\),时间复杂度为
\[ T(n)=O(\frac{n}{m}+m\log n) \]
  当\(m=\sqrt{\frac{n}{\log n}}\)
\[ T(n)_{\min}=O(n\sqrt{n\log n}) \]

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
ull c[100010];
int a[100010];
int n;
void add(int x,ull v)
{
    for(;x<=n;x+=x&-x)
        c[x]+=v;
}
ull sum(int x)
{
    ull s=0;
    for(;x;x-=x&-x)
        s+=c[x];
    return s;
}
int bl;
ull s[1010];
int d[1010][100010];
int l[100010];
int r[100010];
int block[100010];
int left[100010];
int right[100010];
int main()
{
    memset(c,0,sizeof c);
//  freopen("xsy2111.in","r",stdin);
//  freopen("xsy2111.out","w",stdout);
    int m;
    scanf("%d",&n);
    int i;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        add(i,a[i]);
    }
    bl=100;
    m=(n+bl-1)/bl;
    for(i=1;i<=n;i++)
        block[i]=(i+bl-1)/bl;
    for(i=1;i<=m;i++)
    {
        left[i]=(i-1)*bl+1;
        right[i]=min(i*bl,n);
    }
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&l[i],&r[i]);
        s[block[i]]+=sum(r[i])-sum(l[i]-1);
        d[block[i]][l[i]]++;
        if(r[i]<n)
            d[block[i]][r[i]+1]--;
    }
    int j;
    for(i=1;i<=m;i++)
        for(j=2;j<=n;j++)
            d[i][j]+=d[i][j-1];
    int q;
    scanf("%d",&q);
    int op,x,y,k;
    for(i=1;i<=q;i++)
    {
        scanf("%d%d%d",&op,&x,&y);
        if(op==1)
        {
            int v=a[x];
            for(j=1;j<=m;j++)
                s[j]+=ull(y-v)*d[j][x];
            add(x,y-v);
            a[x]=y;
        }
        else
        {
            ull ans=0;
            for(j=block[x];j<=block[y];j++)
                if(left[j]>=x&&right[j]<=y)
                    ans+=s[j];
                else
                {
                    int mi=max(left[j],x);
                    int mx=min(right[j],y);
                    for(k=mi;k<=mx;k++)
                        ans+=sum(r[k])-sum(l[k]-1);
                }
            printf("%llu\n",ans);
        }
    }
    return 0;
}
优质内容筛选与推荐>>
1、构建之法阅读笔记03
2、【京东账户】——Mysql/PHP/Ajax爬坑之添加购物车
3、九、航材_航材申请和库房发料
4、推荐litianping的几篇文章,包括owc统计图,rss技术,项目常用类,petshop架构分析
5、UpdatePanel控件的使用


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号