树状数组的知识总结

单点修改:

  不需要差分

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

  假设现在有一个原数组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 }
代码
原文地址:https://www.cnblogs.com/thunder-110/p/10294042.html