例题一个简单的整数问题
思路
数组大小为 1 0 5 10^5 105,因为要更新区间,所以要利用差分数组。因为要求某个 a i a_i ai,所以把所有的差相加就是答案。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 200;
int a[N], bt[N];
int n, m;
int lowbit(int x) {
return x & (-x);
}
void add(int p, int x) {
for (int i = p; i <= n; i += lowbit(i)) {
bt[i] += x;
}
}
ll sum(int p) {
ll res = 0;
for (int i = p; i; i -= lowbit(i)) {
res += bt[i];
}
return res;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
add(i, a[i] - a[i - 1]);
}
while (m--) {
char op;
cin >> op;
if (op == 'Q') {
int x;
cin >> x;
cout << sum(x) << '\n';
}
else {
int a, b, c;
cin >> a >> b >> c;
add(a, c);
add(b + 1, -c);
}
}
return 0;
}
例题一个简单的整数问题2
思路
既要更新区间,又要求某一个区间的和,所以用树状数组的话,就要玩点骚操作。我们要先想办法求出[1,p]的区间和,然后任意区间和做差即可。
公式推导
a[]表示原数组,b[]表示a数组的差分数组。
因为
a
1
=
b
1
a_1=b_1
a1=b1
a
2
=
b
1
+
b
2
a_2=b_1+b_2
a2=b1+b2
a
3
=
b
1
+
b
2
+
b
3
a_3=b_1+b_2+b_3
a3=b1+b2+b3
…
a
n
=
b
1
+
b
2
+
b
3
+
.
.
.
+
b
n
a_n=b_1+b_2+b_3+...+b_n
an=b1+b2+b3+...+bn
怎么求
∑
i
=
1
n
a
i
\sum_{i=1}^na_i
∑i=1nai呢?
//我们可以先把下面堆式子补成一个完整的矩阵。
b1
b1+b2
b1+b2+b3
...
b1+b2+b3+...+bn
//通过这堆式子:
b1+b2+b3+...+bn
b2+b3+...+bn
b3+...+bn
...
bn
//补成的矩阵:
b1+b2+b3+...+bn
b1+b2+b3+...+bn
b1+b2+b3+...+bn
...
b1+b2+b3+...+bn
//共n+1行
所以求和公式如下,即用完整的矩阵减去新加的式子
s
u
m
(
n
)
=
(
n
+
1
)
∗
(
b
1
+
b
2
+
.
.
.
+
b
n
)
−
(
b
1
+
2
b
2
+
3
b
3
+
.
.
.
+
n
b
n
)
sum(n)=(n+1)*(b_1+b_2+...+b_n)-(b_1+2b_2+3b_3+...+nb_n)
sum(n)=(n+1)∗(b1+b2+...+bn)−(b1+2b2+3b3+...+nbn)
定义
b
t
1
[
i
]
bt1[i]
bt1[i],存储的是
a
[
i
]
−
a
[
i
−
1
]
a[i]-a[i-1]
a[i]−a[i−1],即
b
i
b_i
bi
b
t
2
[
i
]
bt2[i]
bt2[i],存储的是
i
∗
(
a
[
i
]
−
a
[
i
−
1
]
)
i*(a[i]-a[i-1])
i∗(a[i]−a[i−1]),即
i
∗
b
i
i*b_i
i∗bi
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 200;
int a[N];
int n, m;
ll bt1[N], bt2[N];
int lowbit(int x){
return x & -x;
}
void add(ll bt[], int p, ll x) {
for (int i = p; i <= n; i += lowbit(i)) {
bt[i] += x;
}
}
ll sum(ll bt[], int p) {
ll res = 0;
for (int i = p; i; i -= lowbit(i)) {
res += bt[i];
}
return res;
}
ll prefix_sum(int x) {
return sum(bt1, x) * (x + 1) - sum(bt2, x);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
add(bt1, i, a[i] - a[i - 1]);
add(bt2, i, (ll)i * (a[i] - a[i - 1]));
}
while (m--) {
char op;
cin >> op;
if (op == 'Q') {
int l, r;
cin >> l >> r;
cout << prefix_sum(r) - prefix_sum(l - 1) << '\n';
}
else {
int l, r, d;
cin >> l >> r >> d;
add(bt1, l, d);
add(bt1, r + 1, -d);
add(bt2, l, l * d);
add(bt2, r + 1, -(r + 1) * d);
}
}
return 0;
}
例题谜一样的牛
思路
现有
a
[
i
]
a[i]
a[i] 表示第
i
i
i 头牛前面有
a
[
i
]
a[i]
a[i] 头牛比第
i
i
i 头牛高,从后往前推。由于身高只有
1
−
n
1-n
1−n,所以第
i
i
i 头牛的身高是剩下的身高中第
a
[
i
]
+
1
a[i]+1
a[i]+1 小的,确定完第i头牛的身高后,把该身高删除。
我们可以设计一个树状数组,其原型是
b
[
N
]
b[N]
b[N],每个元素值为
1
1
1,若被删除则为
0
0
0。那么剩余身高中,第
k
k
k 小的身高就是当
s
u
m
[
i
]
=
k
sum[i]=k
sum[i]=k 时的
i
i
i。
s
u
m
[
i
]
sum[i]
sum[i] 的下标
i
i
i 代表的是牛真正的身高,
s
u
m
[
i
]
sum[i]
sum[i] 表示的是在
[
1
,
i
]
[1,i]
[1,i] 的身高中,有多少头牛还在,没有被删除。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 300;
int n, a[N];
int bt[N];
int lowbit(int x) {
return x & -x;
}
void add(int p, int x) {
for (int i = p; i <= n; i += lowbit(i)) {
bt[i] += x;
}
}
int sum(int p) {
int res = 0;
for (int i = p; i; i -= lowbit(i)) {
res += bt[i];
}
return res;
}
int bs(int x) {//二分查找剩余身高中第 x 小的身高
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (sum(mid) < x) {
l = mid + 1;
}
else {
r = mid;
}
}
return l;
}
int main() {
cin >> n;
add(1, 1);
for (int i = 2; i <= n; ++i) {
add(i, 1);
cin >> a[i];
}
int ans[N];
for (int i = n; i; --i) {
int p = a[i] + 1;//在剩下的数中,找到第 p 小的数
ans[i] = bs(p);
add(ans[i], -1);
}
for (int i = 1; i <= n; ++i) {
cout << ans[i] <<"\n";
}
return 0;
}