首页 > 其他分享 >二维树状数组

二维树状数组

时间:2024-03-12 22:36:07浏览次数:27  
标签:limits 树状 int sum d% times 二维 maxn 数组

二维树状数组

模板

单点修改,子矩阵查询

暴力的把一维拓展到二维,直接然后按照一维的方法搞就OK,参考代码:

void insert(int x,int y,int z)
{
     for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=m;j+=lowbit(j)) d[i][j]+=z;
}
int getsum(int x,int y)
{
    int sum=0;
    for(int i=x;i;i-=lowbit(i)) for(int j=y;j;j-=lowbit(j))
        sum+=d[i][j];
    return sum;
}

二维差分

类似于一维的差分,我们可以得出:

\[d[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1] \]

子矩阵加,求子矩阵和

修改操作可以类似的转化为差分数组的 \(4\) 个单点修改。

区间查询需要稍微推一下式子:

\[\sum\limits_{i=1}^x\sum\limits_{j=1}^y\sum\limits_{h=1}^i\sum\limits_{k=1}^j d[h][k] \]

这里考虑一下 \(d[h][k]\) 出现的次数。

显然有 \(d[h][k]\) 出现了 \((x-h+1)\times (y-k+1)\) 次。

然后,有:

\[\begin{aligned} & \sum\limits_{i=1}^x\sum\limits_{j=1}^y\sum\limits_{h=1}^i\sum\limits_{k=1}^j d[h][k]\\ & =\sum\limits_{i=1}^x\sum\limits_{j=1}^y d[i][j]\times (x-i+1)\times (y-j+1)\\ & =\sum\limits_{i=1}^x\sum\limits_{j=1}^y d[i][j]\times (xy+x+y+1)-d[i][j]\times i\times (y+1) -d[i][j]\times j\times (x+1) +d[i][j]\times i\times j \end{aligned} \]

所以分别维护 \(d[i][j]\)、\(d[i][j]\times i\)、\(d[i][j]\times j\)、\(d[i][j]\times i\times j\) 的树状数组。

例题

P4514 上帝造题的七分钟

板子题。

#include<bits/stdc++.h>
using namespace std;

const int maxn=2050;

int n,m;

struct treearray
{
    int d[maxn][maxn],di[maxn][maxn],dj[maxn][maxn],dij[maxn][maxn];
    int lowbit(int x){return x&(-x);}
    void insert(int x,int y,int z)
    {
        for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=m;j+=lowbit(j))
        {
            d[i][j]+=z;
            di[i][j]+=z*x;
            dj[i][j]+=z*y;
            dij[i][j]+=z*x*y;
        }
    }
    int getsum(int x,int y)
    {
        int sum=0;
        for(int i=x;i;i-=lowbit(i)) for(int j=y;j;j-=lowbit(j))
            sum+=d[i][j]*(x+1)*(y+1)-di[i][j]*(y+1)-dj[i][j]*(x+1)+dij[i][j];
        return sum;
    }
}T;

void add(int a,int b,int c,int d,int z)
{
    T.insert(a,b,z);
    T.insert(a,d+1,-z);
    T.insert(c+1,b,-z);
    T.insert(c+1,d+1,z);
}
int qry(int a,int b,int c,int d)
{
    return T.getsum(c,d)-T.getsum(a-1,d)-T.getsum(c,b-1)+T.getsum(a-1,b-1);
}

int main()
{
    char op[10];
    cin>>op;
    scanf("%d%d",&n,&m);
    while(scanf("%s",op)!=EOF)
    {
        int a,b,c,d;
        scanf("%d%d%d%d",&a,&b,&c,&d);
        if(op[0]=='L')
        {
            int z;
            scanf("%d",&z);
            add(a,b,c,d,z);
        }
        else printf("%d\n",qry(a,b,c,d));
    }
}

P4054 JSOI2009 计数问题

单点修改的板子题,可以有效区分区间修改的题目。

#include<bits/stdc++.h>
using namespace std;

const int maxn=303;

int n,m;

struct treearray
{
    short d[maxn][maxn];
    int lowbit(int x){return x&(-x);}
    void insert(int x,int y,int z)
    {
        for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=m;j+=lowbit(j)) d[i][j]+=z;
    }
    int getsum(int x,int y)
    {
        int sum=0;
        for(int i=x;i;i-=lowbit(i)) for(int j=y;j;j-=lowbit(j))
            sum+=d[i][j];
        return sum;
    }
    void add(int xa,int ya,int xb,int yb,int z){insert(xa,ya,z);}
    int qry(int xa,int ya,int xb,int yb)
    {
        return getsum(xb,yb)-getsum(xa-1,yb)-getsum(xb,ya-1)+getsum(xa-1,ya-1);
    }
}col[105];

int mp[maxn][maxn];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
    {
        int x;
        scanf("%d",&x);
        mp[i][j]=x;
        col[x].add(i,j,i,j,1);
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int op,xa,ya,xb,yb,c;
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d%d",&xa,&ya,&c);
            col[mp[xa][ya]].add(xa,ya,xa,ya,-1);
            col[c].add(xa,ya,xa,ya,1);
            mp[xa][ya]=c;
        }
        else
        {
            scanf("%d%d%d%d%d",&xa,&xb,&ya,&yb,&c);
            printf("%d\n",col[c].qry(xa,ya,xb,yb));
        }
    }
}

先咕着,做了后面的题再更。

标签:limits,树状,int,sum,d%,times,二维,maxn,数组
From: https://www.cnblogs.com/binbinbjl/p/18069499

相关文章

  • c语言函数传递数组名
    c语言自定义函数中可以在形参中可以使用数组名作为传递代码示例如下#include<stdio.h>floatave(floata[]){ inti; floatb; floatsum=a[0]; for(i=1;i<10;++i) sum=sum+a[i]; b=sum/10; returnb;}intmain(){ floatnum[10],average; inti; for(i=0;i......
  • abc222D 夹在两升序数组之间的升序数组个数
    给定长度为n的两升序数组A[i]和B[i],其中A[i]<=A[i+1],B[i]<=B[i+1],并且0<=A[i]<=B[i]<=3000,找长度为n的数组C[i],满足A[i]<=C[i]<=B[i]。求满足该条件的C的个数,结果对998244353取余。1<=n<=3000设dp[i][j]表示前i个数以j结尾的方案数,那么$dp[i][j]=\sum_{k=0}^{j}dp[i-1][k]$,这......
  • 02-数组、排序、查找
    数组数组是一种引用数据类型,所以数组对象实际上存储在堆内存当中数组实际上是一种容器,可以容纳多个元素数组中存储的是基本数据类型的数据,或者是引用数据类型的引用(不能直接存储Java对象)长度不可变,起始位置是0,最后一个下标是length-1所有的数组对象都有length属性Java中......
  • 树状数组
    树状数组基础基础部分点击查看代码......
  • 【IC验证】数组
    一、非组合型数组1.声明logic[31:0]array[1024];或者logic[31:0]array[1023:0];或者logicarray[31:0][1023:0];理解成一维数组就表示array数组中有1024个数据,每个数据32bit。也可以理解为二维数组。int[1:0][2:0]a1[3:0][4:0]这是一个4×5×2×3维(高维-......
  • 一维数组和二维数组传参是不同的:
    数组传参,传递的实质是首元素的地址。(一)一维数组例:写一个函数对将一个整型数组的内容,全部置为-1,再写一个函数打印数组的内容.#include<stdio.h>voidreset(intarr[],intx)//形参和实参名可以相同也可以不相同{ inti=0; for(size_ti=0;i<x;i++) { arr[i......
  • 蓝桥杯2019年第十届省赛真题-修改数组
    查重类题目,想到用标记数组记录是否出现过但是最坏情况下可能会从头找到小尾巴,时间复杂度O(n2),数据范围106显然超时再细看下题目,我们重复进行了寻找是否出现过,干脆把每个元素出现过的次数k记录下来,直接跳到后k个位置,实现O(n)#include<stdio.h>#include<string.h>#inclu......
  • 【算法】【线性表】【数组】在排序数组中查找元素的第一个和最后一个位置
    1 题目给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1,-1]。你必须设计并实现时间复杂度为 O(logn) 的算法解决此问题。示例1:输入:nums=[5,7,7,8,8,......
  • 高效的服务端生成QRCODE二维码方案-Docker搭建
    朗读全文Yourbrowserdoesnotsupporttheaudioelement.有什么用展现服务端生成QRCODE二维码方案(Docker搭建)可直接使用我搭建的服务端生成QRCODE服务怎么用使用URL:https://c.carlzeng.top:4443/qrcode?size=150&margin=20&txt=www.carlzeng.top或者使用https://q......
  • 生成二维码及二维码添加文本及图片
      生成二维码及二维码添加文本及图片如果要输出流,也可以参考此处packagecom.myFirstSpring.test;importjava.awt.BasicStroke;importjava.awt.Color;importjava.awt.Font;importjava.awt.FontMetrics;importjava.awt.Graphics;importjava.awt.Graphics2D;impo......