• 树状数组


    描述

    参考资料:我是传送门

    1. 一维树状数组的差分写法
    2. 二维树状数组

    注意要差分处理

    一维树状数组

    (1). 单点修改+区间查询

    (2). 区间修改+单点查询

    // 给位置p增加x<br />
    void add(int p,int x)
    { 
      while(p &lt;= n)
        sum[p] += x,p += p&amp;(-p);
      // 求位置p的前缀和
      int ask(int p)
      {
        int res = 0;
      while(p)
        res += sum[p],p -= p&amp;(-p);
       return res;
      }
      // 区间求和
      int range_ask(int l,int r)
      {
        return ask(r)-ask(l-1);
      }
    

    应用“差分”将问题转化

    详情看顶端博客

    // 给位置p增加x
    void add(int p,int x)
    {
    while(p <= n)
    sum[p] += x,p += p&(-p);
    }
    // 求位置p的前缀和
    int ask(int p)
    {
      int res = 0;
      while(p)
        res += sum[p],p -= p&(-p);
      return res;
    }
    // 区间求和
    int range_ask(int l,int r)
    {
      return ask(r)-ask(l-1);
    }
    

     

    (3). 区间修改+区间查询

    继续“差分”

    无论是时间上还是空间上都比带lazy标记的线段树要优

    void add(ll p,ll x)
    {
      for(int i = p;i <= n;i += i&(-i))
        sum1[i] += x,sum2[i] += x*p;
    }
    
    void range_add(ll l,ll r,ll x)
    {
      add(l,x),add(r+1,-x);
    }
    ll ask(ll p)
    {
      ll res = 0;
      for(int i = p;i;i -= i&(-i))
        res += (p+1)*sum1[i]-sum2[i];
      return res;
    }
    ll range_ask(ll l,ll r)
    {
      return ask(r)-ask(l-1);
    }
    

    二维树状数组

    tree[x][y]tree[x][y] : 记录右下角为(x,y)(x,y),高为lowbit(x),宽为lowbit(y)的区间的区间和

    (1). 单点修改+区间查询

    void add(int x,int y,int z)
    {
        int memo_y = y;
        while(x <= n)
        {
            y = memo_y;
            while(y <= m)
            	tree[x][y] += z,y += y&(-y);
            x += x&(-x);
        }
    }
    // 求左上角为(1,1)右下角为(x,y)的矩阵和
    void ask(int x,int y)
    {
        int res = 0,memo_y = y;
        while(x)
        {
            y = memo_y;
            while(y)
                res += tree[x][y],y -= y&(-y);
            x -= x&(-x);
        }
    }
    
    ll range_ask(ll xa, ll ya, ll xb, ll yb)
    {
        return ask(xb,yb) - ask(xb,ya - 1) - ask(xa - 1,yb) + ask(xa - 1,ya - 1);
    }
    

    (2). 区间修改+单点查询

    我们对于一维数组进行差分,是为了使差分数组前缀和等于原数组对应位置的元素。

    那么如何对二维数组进行差分呢?可以针对二维前缀和的求法来设计方案。

    二维前缀和:

    sum[i][j]=sum[i1][j]+sum[i][j1]sum[i1][j1]+a[i][j]

    那么我们可以令差分数组d[i][j]表示 a[i][j]与 a[i1][j]+a[i][j1]a[i1][j1]的差。

    例如下面这个矩阵

     
    1  4  8
    6 7 2
    3 9 5

    对应的差分数组就是

     
     1  3  4
    5 -2 -9
    -3 5 1

    当我们想要将一个矩阵加上x时,怎么做呢?
    下面是给最中间的3*3矩阵加上x时,差分数组的变化:

     
    0  0  0  0  0
    0 +x 0 0 -x
    0 0 0 0 0
    0 0 0 0 0
    0 -x 0 0 +x

    这样给修改差分,造成的效果就是:

     
    0  0  0  0  0
    0 x x x 0
    0 x x x 0
    0 x x x 0
    0 0 0 0 0
    void add(int x,int y,int z)
    {
        int memo_y = y;
        while(x <= n)
        {
            y = memo_y;
            while(y <= m)
            	tree[x][y] += z,y += y&(-y);
            x += x&(-x);
        }
    }
    
    void range_add(int xa,int ya,int xb,int yb,int z)
    {
        add(xa,ya,z);
        add(xa,yb+1,-z);
        add(xb+1,ya,-z);
        add(xb+1,yb+1,z);
    }
    
    int ask(int x,int y)
    {
        int res = 0,memo_y = y;
        while(x)
        {
            y = memo_y;
            while(y)
                res += tree[x][y],y -= y&(-y);
            x -= x&(-x);
        }
        return res;
    }
    

    (3). 区间修改+区间查询

    四个树状数组,分别维护

    d[i][j],d[i][j]i,d[i][j]j,d[i][j]ij

    const int N = 205;
    ll n,m,Q;
    ll t1[N][N],t2[N][N],t3[N][N],t4[N][N];
    void add(ll x,ll y,ll z)
    {
        for(int X = x;X <= n;X += X&(-X))
            for(int Y = y;Y <= m;Y += Y&(-Y))
            {
                t1[X][Y] += z;
                t2[X][Y] += z*x;
                t3[X][Y] += z*y;
                t4[X][Y] += z*x*y;
            }
    }
    
    void range_add(ll xa,ll ya,ll xb,ll yb,ll z)
    {
        add(xa,ya,z);
        add(xa,yb+1,-z);
        add(xb+1,ya,-z);
        add(xb+1,yb+1,z);
    }
    
    ll ask(ll x,ll y)
    {
        ll res = 0;
        for(int i = x;i;i -= i&(-i))
            for(int j = y;j;j -= j&(-j))
                res += (x+1)*(y+1)*t1[i][j]
                	- (y+1)*t2[i][j] - (x+1)*t3[i][j]
                	+ t4[i][j];
        return res;
    }
    
    ll range_ask(ll xa,ll ya,ll xb,ll yb)
    {
        return ask(xb,yb) - ask(xb,ya-1) - ask(xa-1,yb) 
               + ask(xa-1,ya-1);
    }
    
  • 相关阅读:
    PhoneApplicationPage 之观察 触摸事件 GIS
    读书笔记 之 image GIS
    button 样式 GIS
    TextBlock GIS
    textgame GIS
    c#对List或ListArray或string组数 用linq进行分组统计
    彗星撞地球 怀念下Warez组织的经典力作(15G动画压缩成64Kb的那个)
    .NET Framework各版本独立下载.NET Framework 3.5下载.NET Framework 2.0下载
    asp.net 导出excel 表之后 按钮 页面控件失效不可用,没反应的解决办法。
    SQL Sever 各版本下载 SQL Server 2012下载SQL Server 2008下载SQL Server 2005 下载SQL Server 2000 下载
  • 原文地址:https://www.cnblogs.com/duny31030/p/14304952.html
Copyright © 2020-2023  润新知