zoukankan      html  css  js  c++  java
  • 树状数组的知识总结

    单点修改:

      不需要差分

    区间修改,单点查询: //参考

      假设现在有一个原数组a(假设a[0] = 0),有一个数组d,d[i] = a[i] - a[i-1],那么

    a[i] = d[1] + d[2] + .... + d[i]

    d数组就是差分数组

    所以求a[i]就可以用树状数组维护d[i]的前缀和

    区间修改,单点查询:

    根据d的定义,对[l,r]区间加上x,那么a[l]和a[l-1]的差增加了x,a[r+1]与a[r]的差减少了x,所以就对差分数组的前缀和进行修改

    设c是差分数组的前缀和

    注意:差分 输入的时候要以差的形式

    for(int i=1;i<=n;i++)
    {
        scanf("%d",&y);
        add(i,y-x);
        x=y;
    }
    void add(int x,int k)
    {
        for (int i = 1;i <= n;i += lowbit(i)) c[i] += k;
    }
    {
        add(l,x);  //差分
        add(r+1,-x);
    }
    int sum(int x)
    {
        int ans = 0;
        for (int i = x;i > 0;i -= lowbit(i)) ans += c[i];
        return ans;
    }

    区间修改,区间查询:

      根据上面的差分数组的定义可以得到:

    a[1] + a[2] + a[3] + ... + a[k] = d[1] + d[1] + d[2] + d[1] + d[2] + d[3] + ... + d[1] + d[2] + d[3] + ... + d[k]

                  = Σ(k - i + 1) * d[i]  (i从1到k)

    变化一下 Σa[i] (i从1到k) = Σ(k+1) * d[i] - i * d[i] (i从1到k)

    d[i]可以用一个前缀和维护,i * d[i]也可以用一个前缀和进行维护,所以区间修改,区间查询就变得很方便了

    假设c1维护d[i]的前缀和,c2维护d[i] * i的前缀和

    void add(int x,int y)
    {
        for (int i = x;i <= n;i += lowbit(i)) c1[i] += y,c2[i] += x * y;
    }
    {
        add(l,x);
        add(r+1,-x);
    }
    int sum(int x)
    {
        int ans1 = 0;
        int ans2 = 0;
        for (int i = x;i > 0;i -= lowbit(i))
        {
            ans1 += (x + 1) * c1[i];
            ans2 += c2[i];
        }
        return ans1 - ans2;
    }

    树状数组求 逆序对:

    方法1的博客 (但有0就死循环了,所以前提a[i]>0)

      但又局限性,a[ i ]的最大值不能很大,否则数组会爆

     因此,引进离散化+树状数组

    方法2的博客

      a[i].num  3 7 2 4 6

      a[i].pos   1 2 3 4 5

     对结构体a按num值从小到大排序,得到:

      a[i].num  2 3 4 6 7

      a[i].pos   3 1 4 5 2

     for(int i=1;i<=n;i++)  aa[ a[i].pos ]=i;   会得到:

      i:     1 2 3 4 5

      aa[i]:    2 5 1 3 4

    这样的 aa[i]中各位数字 2 5 1 3 4的相对大小和原数组a[i].num  3 7 2 4 5 是一样的。这样做减少了不必要的空间。 a[i] 可为0

    去重离散化的两种方法:

    struct node
    {
        int v,p;
    }aa[N];
    
    bool cmp(node x,node y)
    {
        return x.v<y.v;
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++) scanf("%d",&aa[i].v),aa[i].p=i;
        sort(aa+1,aa+1+n,cmp);
        int cnt=0;
        for(int i=1; i<=n; i++) {
            if(aa[i].v != aa[i-1].v) {
                cnt++;
            }
            a[aa[i].p] = cnt;
        }
    }
    去重离散化1
    for(i=1;i<=n;++i) cin>>a[i], b[i]=a[i];
    
    void discretize(){
        sort(b+1,b+1+n);
        unique(b+1,b+1+n)-b-1;
        for(i=1;i<=n;++i) a[i]=lower_bound(b+1,b+1+n,a[i])-b;
    }
    去重离散化2
    vector<int> v;
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end())  , v.end());
    ...=lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
    去重离散化3

    第二种去重不太彻底,5 8 8 10,离散化之后是 1 2 2 4

    hdu二维树状数组题:新年彩灯2

     思路:二维转为多个一维的就行了

     1 #include<iostream>
     2 #include<cstdio>
     3 #include <cctype>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<cmath>
     7 #include<string>
     8 #include<cmath>
     9 #include<set>
    10 #include<vector>
    11 #include<stack>
    12 #include<queue>
    13 #include<map>
    14 using namespace std;
    15 #define ll long long
    16 #define mem(a,x) memset(a,x,sizeof(a))
    17 #define se second
    18 #define fi first
    19 const ll mod=1e9+7;
    20 const int INF= 0x3f3f3f3f;
    21 const int N=1e6+5;
    22 
    23 int c[1005][1005];
    24 int n,m;
    25 
    26 int lowbit(int i)
    27 {
    28     return i&-i;
    29 }
    30 
    31 void add(int x,int y,int k)
    32 {
    33     for(int i=y;i<=n;i+=lowbit(i)) c[x][i]+=k;
    34 }
    35 
    36 int sum(int x,int y)
    37 {
    38     int sum=0;
    39     for(int i=y;i>0;i-=lowbit(i)) sum+=c[x][i];
    40     return sum;
    41 }
    42 
    43 int main()
    44 {
    45     int x,y,k,q,a1,a2,b1,b2;
    46     //int cnt=0;
    47     //printf("Case %d:
    ",++cnt);    
    48     while(m--)
    49     {
    50         cin>>q;
    51         if(q==1)
    52         {
    53             scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
    54             if(a1>a2) a1=a1^a2,a2=a1^a2,a1=a1^a2;
    55             if(b1>b2) b1=b1^b2,b2=b1^b2,b1=b1^b2;
    56 
    57             for(int i=a1;i<=a2;i++)
    58             {
    59                 add(i,b1,1);
    60                 add(i,b2+1,-1);
    61             }
    62         }
    63         else
    64         {
    65             scanf("%d%d",&x,&y);
    66             if(sum(x,y)%2==0) cout<<0;
    67             else cout<<1;
    68             
    69             cout<<endl;
    70         }
    71     }
    72 }
    代码
  • 相关阅读:
    Vue使用watch监听一个对象中的属性
    小程序 显示对话框 确定-取消
    【微信小程序】 wx:if 与 hidden(隐藏元素)区别
    vue项目移植tinymce踩坑
    XMLHttpRequest.withCredentials 解决跨域请求头无Cookie的问题
    appJSON["window"]["navigationBarTextStyle"] 字段需为 black 或 white
    Java写 插入 选择 冒泡 快排
    编码表理解
    Centos yum安装java jdk1.8
    Java Hibernate和.Net EntityFramework 如何在提交事务之前 就拿到需要新增实体的Id
  • 原文地址:https://www.cnblogs.com/thunder-110/p/10294042.html
Copyright © 2011-2022 走看看