树状数组题目小结 - 入门篇(模板题)
A. POJ-2352
B. POJ-3067
C. luogu-3368-树状数组 2
D. hdu-1166-敌兵布阵
E. hdu-1556-Color the ball
F. POJ-3468
G. POJ-2481
H. POJ -2299
I. POJ -1990
J. POJ-2155
K. POJ-3321
A. POJ-2352
点修改 + 区间和
/*
nero
2019-8-20 20:39:45
1A
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
//#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 15005;
int n,m;
int c[32005], ans[MAXN];
int lowbit(int x) {
return x & (-x);
}
void update(int x) {
int i = x;
while(i <= 32001) {
c[i]++;
i += lowbit(i);
}
return;
}
int Query(int x) {
int i = x;
int sum = 0;
while(i >= 1) { // 把 0 加了 1
sum += c[i];
i -= lowbit(i);
}
return sum;
}
int main() {
int T;
int x,y;
scanf("%d", &n);
for(int i = 1; i <= 32001; i++) c[i] = 0;
for(int i = 1; i <= n; i++) {
scanf("%d%d", &x, &y);
x++,y++;
ans[Query(x)]++;
update(x);
}
for(int i = 0; i < n; i++) {
printf("%d\n", ans[i]);
}
return 0;
}
B. POJ-3067
点修改+区间和
/*
nero
2019-8-20 17:40
long long WA1
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
//#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 1005;
int n,m;
ll c[MAXN];
struct node {
int x, y;
bool operator<(const node &B)const {
return x == B.x ? y < B.y : x < B.x;
}
}a[MAXN * MAXN];
int lowbit(int x) {
return x & (-x);
}
void update(int x) {
int i = x;
while(i <= m) {
c[i]++;
i += lowbit(i);
}
return;
}
ll Query(int x) {
int i = x;
ll sum = 0;
while(i >= 1) {
sum += c[i];
i -= lowbit(i);
}
return sum;
}
int main() {
int T, K;
scanf("%d", &T);
for(int tt = 1; tt <= T; tt++) {
scanf("%d%d%d", &n, &m, &K);
for(int i = 1; i <= m; i++) c[i] = 0;
for(int i = 1; i <= K; i++) {
scanf("%d%d", &a[i].x, &a[i].y);
}
sort(a + 1, a + K + 1);
ll ans = 0;
for(int i = K; i >= 1; i--) {
ans += Query(a[i].y - 1);
update(a[i].y);
}
printf("Test case %d: %lld\n",tt, ans);
}
return 0;
}
C. luogu-3368-树状数组 2
区间加+点查询
/*
nero
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 500005;
int n, m;
ll c[MAXN<<2], a[MAXN];
int lowbit(int x) {
return x&(-x);
}
void update(int k, ll x) {
for(int i = k; i <= n; i += lowbit(i)) {
c[i] += x;
}
return;
}
ll Query_sum(int k) {
ll sum = 0;
for(int i = k; i >= 1; i -= lowbit(i)) {
sum += c[i];
}
return sum;
}
int main() {
int l, r, op;
ll k;
a[0] = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
update(i,a[i] - a[i-1]);
}
while(m--) {
scanf("%d", &op);
if(op == 1) {
scanf("%d%d%lld", &l, &r, &k);
update(l, k);
update(r+1, -k);
}
else {
scanf("%d", &l);
printf("%lld\n", Query_sum(l));
}
}
return 0;
}
D. hdu-1166-敌兵布阵
单点修改+区间和
前两天做线段树刚刚做过,又遇到了。
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 50005;
int n;
int c[MAXN << 2], a[MAXN];
int lowbit(int x) {
return x&(-x);
}
void update(int i, int x) {
for(int k = i; k <= n; k += lowbit(k)) {
c[k] += x;
}
}
int Query_sum(int i) {
int ans = 0;
for(int k = i; k >= 1; k -= lowbit(k)) {
ans += c[k];
}
return ans;
}
int main() {
int T, x, i, l, r;
char op[10];
scanf("%d", &T);
for(int tt = 1; tt <= T; tt++){
printf("Case %d:\n",tt);
scanf("%d", &n);
for(int i = 1; i <= n*4; i++) {
c[i] = 0;
}
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
update(i, a[i]);
}
while(scanf("%s",op) != EOF) {
if(op[0] == 'E') break;
if(op[0] == 'A') {
scanf("%d%d", &i, &x);
update(i,x);
}
if(op[0] == 'S') {
scanf("%d%d", &i, &x);
update(i,-x);
}
if(op[0] == 'Q') {
scanf("%d%d", &l, &r);
printf("%d\n",Query_sum(r) - Query_sum(l-1));
}
}
}
return 0;
}
E. hdu-1556-Color the ball
区间加 + 单点查询
/*
nero
2019-8-17 11:07:15
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 100005;
int n;
int c[MAXN];
int lowbit(int x) {
return x&(-x);
}
void update(int k, int x) {
for(int i = k; i <= n; i += lowbit(i)) {
c[i] += x;
}
return;
}
ll Query_sum(int k) {
ll sum = 0;
for(int i = k; i >= 1; i -= lowbit(i)) {
sum += c[i];
}
return sum;
}
int main() {
// freopen("in.txt", "r", stdin);
int l, r;
while(scanf("%d", &n) != EOF) {
if(n == 0) break;
for(int i = 0; i <= n; i++) c[i] = 0;
for(int i = 1; i <= n; i++) {
scanf("%d%d", &l, &r);
update(l, 1);
update(r+1, -1);
}
for(int i = 1; i <= n; i++) {
printf("%d", Query_sum(i));
if(i < n) printf(" ");
}
printf("\n");
}
return 0;
}
/*
*/
F. POJ-3468
区间加 + 区间和
/*
nero
2019-8-23 20:43:49
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
//#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 100005;
int n, m;
ll sum1[MAXN], a[MAXN], sum2[MAXN];
int lowbit(int x) {
return x&(-x);
}
void update(int k, ll x) {
for(int i = k; i <= n; i += lowbit(i)) {
sum1[i] += x;
sum2[i] += x * k;
}
return;
}
ll Query_sum(int k) {
ll sum = 0;
for(int i = k; i >= 1; i -= lowbit(i)) {
sum += sum1[i] * (k + 1) - sum2[i];
}
return sum;
}
int main() {
int l, r;
char op[2];
ll k, all = 0;
a[0] = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
update(i,a[i] - a[i-1]);
}
while(m--) {
scanf("%s", &op);
if(op[0] == 'C') {
scanf("%d%d%lld", &l, &r, &k);
update(l, k);
update(r+1, -k);
}
else {
scanf("%d%d", &l, &r);
all = Query_sum(r) - Query_sum(l-1);
printf("%lld\n", all);
}
}
return 0;
}
G. POJ-2481
          
\;\;\;\;\;
区间覆盖,点修改+区间和
          
\;\;\;\;\;
按照左端点由小到大排序,每次只要看前面有多少右端点比当前的右端点大,最后更新右端点的值。
          
\;\;\;\;\;
有个需要注意的地方就是,两个区间可能会重合。这个时候就不能算。
          
\;\;\;\;\;
这个题有点玄学,按照我的写法,行末不输出多余的空格,在G++里面会T,C++可过。换种写法也可过。听学长说一般不用注意这个点。
/*
nero
2019-8-23 21:21:30
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
//#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 100005;
struct node {
int l, r;
int p;
bool operator < (const node &B) const {
return l == B.l ? r > B.r : l < B.l;
}
}t[MAXN];
int n, Max;
int c[MAXN], ans[MAXN];
int lowbit(int x) {
return x & (-x);
}
void update(int k) {
for(int i = k; i <= Max; i += lowbit(i)) {
c[i]++;
}
return;
}
int Query_sum(int k) {
int sum = 0;
for(int i = k; i >= 1; i -= lowbit(i)) {
sum += c[i];
}
return sum;
}
int main() {
while(scanf("%d",&n) != EOF) {
if(n == 0) break;
Max = 0;
for(int i = 1; i <= n; i++) {
scanf("%d%d", &t[i].l, &t[i].r);
t[i].l++, t[i].r++;
Max = max(Max, t[i].r);
t[i].p = i;
}
sort(t + 1, t + n + 1);
for(int i = 1; i <= Max; i++) c[i] = 0;
for(int i = 1; i <= n; i++) {
if(i > 1 && t[i].l == t[i-1].l && t[i].r == t[i-1].r) {
ans[t[i].p] = ans[t[i-1].p];
}
else ans[t[i].p] = (i-1) - Query_sum(t[i].r-1);
update(t[i].r);
}
for(int i = 1; i <= n; i++) { // 这里交 G++ 会 T,C++ 可过
printf("%d", ans[i]);
if(i < n) printf(" ");
}
printf("\n");
}
return 0;
}
H. POJ -2299
          
\;\;\;\;\;
点修改+区间和
          
\;\;\;\;\;
求逆序对,离散化,去重用了 unique 函数
/*
nero
2019-8-17 10:18:08
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
//#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 500005;
int n;
int c[MAXN], a[MAXN];
vector<int>v;
int lowbit(int x) {
return x&(-x);
}
void update(int k, int x) {
for(int i = k; i <= n; i += lowbit(i)) {
c[i] += x;
}
return;
}
ll Query_sum(int k) {
ll sum = 0;
for(int i = k; i >= 1; i -= lowbit(i)) {
sum += c[i];
}
return sum;
}
int main() {
// freopen("in.txt", "r", stdin);
while(scanf("%d", &n) != EOF) {
if(n == 0) break;
v.clear();
ll ans = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
v.push_back(a[i]);
c[i] = 0;
}
sort(v.begin(), v.end());
unique(v.begin(),v.end()); // 去重
for(int i = n; i >= 1; i--) {
int p = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1;
ans += Query_sum(p-1);
update(p,1);
}
printf("%lld\n", ans);
}
return 0;
}
I. POJ -1990
          
\;\;\;\;\;
点修改+区间和
          
\;\;\;\;\;
这个题需要推一下。
          
\;\;\;\;\;
a
n
s
=
∑
i
=
1
n
∑
j
=
1
i
−
1
m
a
x
(
v
i
,
v
j
)
⋅
∣
x
i
−
x
j
∣
ans = \sum_{i=1}^{n}\sum_{j=1}^{i-1}max(v_{i}, v_{j}) \cdot |x_{i} - x_{j}|
ans=∑i=1n∑j=1i−1max(vi,vj)⋅∣xi−xj∣, 答案里面有两个因素,一个是阈值,一个是距离。某一个元素
(
v
i
,
x
i
)
(v_{i}, x_{i})
(vi,xi) 对于答案的贡献有多少,要看其它元素
v
j
v_{j}
vj 和
v
i
v_{i}
vi 的大小关系。只要对于每个元素,求出它关于那些
v
j
<
v
i
v_{j} < v_{i}
vj<vi 的元素的贡献就好。比它大的会在大的那里求。
          
\;\;\;\;\;
对于所有
v
j
<
v
i
v_{j} < v_{i}
vj<vi 的元素,答案就是
∑
v
i
⋅
∣
x
i
−
x
j
∣
\sum v_{i} \cdot |x_{i} - x_{j}|
∑vi⋅∣xi−xj∣,其中
v
i
v_{i}
vi 可以提取出来,这样就是求
∑
∣
x
i
−
x
j
∣
\sum |x_{i} - x_{j}|
∑∣xi−xj∣;
          
\;\;\;\;\;
对于
x
i
>
x
j
x_{i} > x_{j}
xi>xj 的元素,
∑
∣
x
i
−
x
j
∣
=
∑
x
i
−
∑
x
j
=
c
n
t
1
⋅
x
i
−
∑
x
j
\sum |x_{i} - x_{j}| = \sum x_{i} - \sum x_{j} = cnt1 \cdot x_{i} - \sum x_{j}
∑∣xi−xj∣=∑xi−∑xj=cnt1⋅xi−∑xj,其中
c
n
t
1
cnt1
cnt1 为
x
i
>
x
j
x_{i} > x_{j}
xi>xj 的数量;
          
\;\;\;\;\;
对于
x
i
<
x
j
x_{i} < x_{j}
xi<xj 的元素,
∑
∣
x
i
−
x
j
∣
=
∑
x
j
−
∑
x
i
=
∑
x
j
−
c
n
t
2
⋅
x
i
\sum |x_{i} - x_{j}| = \sum x_{j} - \sum x_{i} = \sum x_{j} - cnt2 \cdot x_{i}
∑∣xi−xj∣=∑xj−∑xi=∑xj−cnt2⋅xi,其中
c
n
t
2
cnt2
cnt2 为
x
i
<
x
j
x_{i} < x_{j}
xi<xj 的数量;
          
\;\;\;\;\;
用树状数组维护每个
x
i
x_{i}
xi 的 数量 以及 前缀和 就好。
/*
nero
2019-8-26 10:27:12
long long 用了 %d WA1
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
//#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 20005;
using namespace std;
int n;
ll sum1[MAXN], sum2[MAXN];
struct node {
ll x, c;
bool operator < (const node &B) const {
return c < B.c;
}
}a[MAXN];
int lowbit(int x) {
return x & (-x);
}
void update(int k, int x, ll sum[]) {
int i = k;
while(i < MAXN) {
sum[i] += x;
i += lowbit(i);
}
return;
}
ll Query(int k, ll sum[]) {
ll ans = 0;
int i = k;
while(i > 0) {
ans += sum[i];
i -= lowbit(i);
}
return ans;
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%lld%lld", &a[i].c, &a[i].x);
}
sort(a + 1, a + n + 1);
ll sumx = 0, ans = 0;
for(int i = 1; i <= n; i++) {
ll cnt1 = Query(a[i].x, sum1), cnt2 = i - 1 - cnt1;
ll pre = Query(a[i].x, sum2);
ll dis = a[i].x * cnt1 - pre + sumx - pre - cnt2 * a[i].x;
ans += a[i].c * dis;
sumx += a[i].x;
update(a[i].x, 1, sum1); // 个数
update(a[i].x, a[i].x, sum2);
}
printf("%lld\n", ans);
return 0;
}
J. POJ-2155
          
\;\;\;\;\;
操作1:在一个矩阵里面,元素有两种状态,每次改变一个矩形内的元素的状态。
          
\;\;\;\;\;
操作2:查询某点状态。
          
\;\;\;\;\;
简单的二维树状数组
          
\;\;\;\;\;
区间加+单点查询
/*
nero
2019-8-20 16:49:18
数组清空 WA1
格式错误 WA1
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
//#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 1005;
int n;
int c[MAXN][MAXN];
int lowbit(int x) {
return x & (-x);
}
void update(int x, int y) {
int i = x;
while(i <= n) {
int j = y;
while(j <= n) {
c[i][j]++, j += lowbit(j);
}
i += lowbit(i);
}
return;
}
int Query(int x, int y) {
int sum = 0;
int i = x;
while(i >= 1) {
int j = y;
while(j >= 1) {
sum += c[i][j], j -= lowbit(j);
}
i -= lowbit(i);
}
return sum;
}
int main() {
int T, Q;
int x1, y1, x2, y2, x, y;
char op[3];
scanf("%d", &T);
while(T--) {
memset(c,0,sizeof(c));
scanf("%d%d", &n, &Q);
while(Q--) {
scanf("%s", op);
if(op[0] == 'C') {
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
update(x1,y1);
update(x1,y2+1);
update(x2+1,y1);
update(x2+1,y2+1);
}
else {
scanf("%d%d", &x, &y);
int sum = Query(x,y);
printf("%d\n", sum & 1);
}
}
if(T) printf("\n");
}
return 0;
}
K. POJ-3321
          
\;\;\;\;\;
dfs+单点修改+区间和
          
\;\;\;\;\;
一开始想的是,每dfs一下,每次改变状态的时候只要更新其父节点即可。T了一发后才想到,一条链岂不是。。。
          
\;\;\;\;\;
用 dfs 给点重新编号,寻找一个点的子树中编号最小和最大的点,就得到了这个点的左右区间,(感觉有点像多叉的线段树),问某个子树的苹果数量就变成了求区间和。
          
\;\;\;\;\;
这个题卡vector,改成数组之后就过了,不过评论里面说,用 vector < vector >int 也可过。(连续两天连续卡两个题真的很难受)
/*
nero
2019-8-23 23:47:37
dfs 会 T, 只有一条链
vector 会 T,用数组
*/
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
//#include <bits/stdc++.h>
using namespace std;
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
#define Pi acos(-1)
#define iINF 0x3f3f3f3f
#define lINF 0x3f3f3f3f3f3f3f
#define EPS 0.000000001
#define pii pair<int,int>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 1000005;
int n, cnt = 1, num = 1;
int l[MAXN], r[MAXN], c[MAXN], a[MAXN], head[MAXN];
struct edge {
int x, y, nxt;
}Edge[MAXN << 1];
void Add_edge(int x, int y) {
Edge[num].x = x;
Edge[num].y = y;
Edge[num].nxt = head[x];
head[x] = num++;
}
void dfs(int x, int fa) {
for(int i = head[x] ; i != -1; i = Edge[i].nxt) {
int xx = Edge[i].y;
if(xx == fa) continue;
l[xx] = r[xx] = ++cnt;
dfs(xx, x);
r[x] = max(r[x], r[xx]);
}
return;
}
int lowbit(int x) {
return x & (-x);
}
void update(int k, int x) {
while(k <= n) {
c[k] += x;
k += lowbit(k);
}
return;
}
int Query_sum(int k) {
int sum = 0;
while(k) {
sum += c[k];
k -= lowbit(k);
}
return sum;
}
int main() {
char op[3];
int k, x, y, m;
while(scanf("%d", &n) != EOF) {
num = 1;
for(int i = 1; i <= n; i++) {
c[i] = 0;
l[i] = r[i] = 0;
head[i] = -1;
}
for(int i = 1; i < n; i++) {
scanf("%d%d", &x, &y);
Add_edge(x,y);
Add_edge(y,x);
}
cnt = 1;
l[1] = r[1] = 1;
dfs(1, 0);
for(int i = 1; i <= n; i++) {
a[i] = 1;
update(l[i], 1);
}
scanf("%d", &m);
while(m--) {
scanf("%s%d", op, &k);
if(op[0] == 'C') {
if(a[k] == 0) update(l[k], 1);
else update(l[k], -1);
a[k] = 1 - a[k];
}
else printf("%d\n", Query_sum(r[k]) - Query_sum(l[k]-1));
}
}
return 0;
}
题目参考:【140508专题】树状数组——题目集