[模板] 树状数组及其应用
树状数组
树状数组又是一个区间查询修改利器 前缀和的维护 差分的维护
又强又方便
二进制组合原理
struct bit
{
ll c[MAXN], N; //c树, N数组长
bit() {}
bit(int n) { N = n; fill(c, c + N + 1, 0); } //初始化数组长
int lowbit(int x) { return x & -x; }
void update(int pos, ll val)
{
for( ;pos <= N; pos += lowbit(pos))
c[pos] += val;
}
ll ask(int pos)
{
ll ret = 0;
for( ;pos; pos -= lowbit(pos))
ret += c[pos];
return ret;
}
};
维护二维前缀和
/*
Zeolim - An AC a day keeps the bug away
*/
//#pragma GCC optimize(2)
//#pragma GCC ("-W1,--stack=128000000")
#include <bits/stdc++.h>
using namespace std;
#define mp(x, y) make_pair(x, y)
#define fr(x, y, z) for (int x = y; x < z; ++x)
#define pb(x) push_back(x)
#define mem(x, y) memset(x, y, sizeof(x))
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef std::pair<int, int> pii;
typedef std::vector<int> vi;
// typedef __int128 ill;
const ld PI = acos(-1.0);
const ld E = exp(1.0);
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 386910137;
const ull P = 13331;
const int MAXN = 5e3 + 100;
int n, m;
int low(int x) { return x & -x; }
ll c[MAXN][MAXN] = { 0 };
void update(int x, int y, int z) {
for (; x <= n; x += low(x)) {
for (int j = y; j <= m; j += low(j)) {
c[x][j] += z;
}
}
}
ll ask(int x, int y) {
ll ret = 0;
for (; x > 0; x -= low(x)) {
for (int j = y; j > 0; j -= low(j)) {
ret += c[x][j];
}
}
return ret;
}
int main() {
scanf("%d%d", &n, &m);
int opt, x, y, z;
while (scanf("%d", &opt) == 1) {
if (opt == 1) {
scanf("%d%d%d", &x, &y, &z);
update(x, y, z);
} else {
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
ll ans = ask(x2, y2) - ask(x1 - 1, y2) - ask(x2, y1 - 1) + ask(x1 - 1, y1 - 1);
printf("%lld\n", ans);
}
}
return 0;
}
/*
3 4
1 2 3
1 2 3
2 3 5
2 3 7
*/
用法 1,区间查询和 [fst, lst]
前缀和相减即可 sum = crr[x] - crr[y - 1]
用法2 单点更新 add(pos, val)
用法3 区间加和 [fst, lst] add(fst, val), add(lst + 1, -val); 差分
用法4 区间加和的单点查询 ans = ask(k) + arr[k];
树状数组求逆序数
树状数组可以求前缀和,
那么设一个数组C记录当前下标的数的个数
所以从后往前扫数组可以logn的查询出当前值之后 的 比当前值小的所有数的前缀和(逆序数)
扫完再把当前值插入二进制下标树即可
当值太大的时候可以离散 这时候时间复杂度肯定就比归并要大的多了, 不过可以解决归并背不下来的情况
/*
Zeolim - An AC a day keeps the bug away
*/
//#pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <string>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <sstream>
#include <map>
#include <ctime>
#include <vector>
#include <fstream>
#include <list>
#include <iomanip>
#include <numeric>
using namespace std;
typedef long long ll;
typedef long double ld;
const int INF = 0x3f3f3f3f;
const ld PI = acos(-1.0);
const ld E = exp(1.0);
const int MAXN = 1e6 + 10;
ll arr[MAXN] = {0};
ll brr[MAXN] = {0};
ll c[MAXN] = {0};
ll rp(ll x, ll n)
{
return lower_bound(brr, brr + n, x) - brr + 1;
}
ll lobit(ll x)
{
return x & -x;
}
void add(ll x, ll n)
{
for(; x <= n; x += lobit(x))
++c[x];
}
ll ask(ll x)
{
ll ret = 0;
for( ; x; x -= lobit(x))
ret += c[x];
return ret;
}
int main()
{
//ios::sync_with_stdio(false);
//cin.tie(0); cout.tie(0);
//freopen("D://test.in", "r", stdin);
//freopen("D://test.out", "w", stdout);
int n;
cin >> n;
for(int i = 0; i < n; ++i)
{
cin >> arr[i];
}
memcpy(brr, arr, sizeof(arr));
sort(brr, brr + n);
int ln = unique(brr, brr + n) - brr;
ll ans = 0;
for(int i = n - 1; i >= 0; --i)
{
int pos = rp(arr[i], ln);
ans += ask(pos - 1);
add(pos, ln);
}
cout << ans << '\n';
return 0;
}