这是代码源数据结构中级的第一课,树状数组。
首先第一题,是一道很基础的模板题。
是用树状数组作最基础的查询前缀和和单点修改,代码如下:
/*
coder:sunshine
school:njupt
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n' //交互题删掉
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int a[N];
ll c[N];
int n, q;
ll query(int x) // 1...x
{
ll s = 0;
for (; x; x -= x & (-x))
{
s += c[x];
}
return s;
}
void modify(int x, ll s) // a[x]+=s;
{
for (; x <= n; x += x & (-x))
{
c[x] += s;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
modify(i, a[i]);
}
for (int i = 0; i < q; i++)
{
int x;
cin >> x;
if (x == 1)
{
int p, q;
cin >> p >> q;
modify(p, q - a[p]);
a[p] = q;
}
else
{
int m;
cin >> m;
cout << query(m) << endl;
}
}
return 0;
}
于是就有了对树状数组的最常见的应用
−
>
O
(
n
l
o
g
n
)
->O(nlogn)
−>O(nlogn)求逆序对个数。
做法:改为后缀查询,设a[i]为后面有几个数大于a[i]。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int n;
int a[N];
ll c[N];
ll query(int x)
{
ll s = 0;
for (; x; x -= x & (-x))
{
s += c[x];
}
return s;
}
void modify(int x, ll s)
{
for (; x <= n; x += x & (-x))
{
c[x] += s;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
a[i] = n + 1 - a[i];
}
ll ans = 0;
for (int i = 1; i <= n; i++)
{
ans += query(a[i]);
modify(a[i], 1);
}
cout << ans << endl;
return 0;
}
第三题是树状数组2
做法:结合差分数组来做。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
typedef unsigned long long u64;
const int N = 2e5 + 10;
int n, q;
template <class T>
struct BIT
{
T c[N];
int size;
void resize(int s)
{
size = s;
}
T query(int x)
{
assert(x <= n);
T s = 0;
for (; x; x -= x & (-x))
{
s += c[x];
}
return s;
}
void modify(int x, T s)
{
assert(x != 0);
for (; x <= size; x += x & (-x))
{
c[x] += s;
}
}
};
BIT<u64> c1, c2; // c1 d[i] c2 i*d[i]
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> q;
c1.resize(n);
c2.resize(n);
for (int i = 1; i <= q; i++)
{
int ty;
cin >> ty;
if (ty == 1)
{
int l, r;
u64 d;
cin >> l >> r >> d;
c1.modify(l, d);
c1.modify(r + 1, -d);
c2.modify(l, l * d);
c2.modify(r + 1, (r + 1) * (-d));
}
else
{
int x;
cin >> x;
u64 ans = (x + 1) * c1.query(x) - c2.query(x);
cout << ans << endl;
}
}
return 0;
}
第四题就是树状数组二分
做法:第一个操作就是正常的单点修改,第二个操作一眼可以二分,因为前缀和具有二分性,但是复杂度到 O ( l o g n l o g n ) O(lognlogn) O(lognlogn)了,所以需要一点小技巧。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int n, q;
int a[N];
ll c[N];
void modify(int x, int s) // a[x]+=s;
{
for (; x <= n; x += x & (-x))
{
c[x] += s;
}
}
int query(ll s)
{
ll t = 0;
int pos = 0;
for (int j = 18; j >= 0; j--)
{
if (pos + (1 << j) <= n && t + c[pos + (1 << j)] <= s)
{
pos += (1 << j);
t += c[pos];
}
}
return pos;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
modify(i, a[i]);
}
for (int i = 0; i < q; i++)
{
int ty;
cin >> ty;
if (ty == 1)
{
int x, d;
cin >> x >> d;
modify(x, d - a[x]);
a[x] = d;
}
else
{
ll s;
cin >> s;
cout << query(s) << endl;
}
}
return 0;
}
第五题是高维树状数组:
做法:题目就是求二维前缀和,跟一维一样类推就行,代码如下:
/*
coder:sunshine
school:njupt
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n' //交互题删掉
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
int n, m, q;
const int N = 510;
int a[N][N];
ll c[N][N];
ll query(int x, int y)
{ // 1 ... x
ll s = 0;
for (int p = x; p; p -= p & (-p))
for (int q = y; q; q -= q & (-q))
s += c[p][q];
return s;
}
void modify(int x, int y, ll s)
{
for (int p = x; p <= n; p += p & (-p))
{
for (int q = y; q <= m; q += q & (-q))
{
c[p][q] += s;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m >> q;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> a[i][j];
modify(i, j, a[i][j]);
}
}
for (int i = 0; i < q; i++)
{
int ty;
cin >> ty;
if (ty == 1)
{
int x, y, d;
cin >> x >> y >> d;
modify(x, y, d - a[x][y]);
a[x][y] = d;
}
else
{
int x, y;
cin >> x >> y;
cout << query(x, y) << endl;
}
}
return 0;
}