树状数组的知识总结
单点修改:
不需要差分
区间修改,单点查询: //参考
假设现在有一个原数组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 ]的最大值不能很大,否则数组会爆
因此,引进离散化+树状数组
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
去重离散化的两种方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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;
第二种去重不太彻底,5 8 8 10,离散化之后是 1 2 2 4
思路:二维转为多个一维的就行了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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:\n",++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 }