目录:
树状数组是什么?
(待补)
模板题
#include<bits/stdc++.h>
#include<ext/rope>
#define ll long long
#define inf 0x3f3f3f3f
#define endl '\n'
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
using namespace std;
const int N = 6e5 + 10;
int n, m;
ll a[N], tree[N];
int lowbit(int x) //找到x的二进制表示末位1
{
return x & (-x);
}
void build() //建树
{
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
updata(i, a[i]);
}
}
void updata(int x, int y) //树的维护
{
for(; x <= n; x += lowbit(x))
tree[x] += y;
}
//此处可以如是想:lowbit取出的是当前x的最低含一位
//权值位,相加后等于向高位进位,并且已有的数位永远为零
//这就可以推出:每当x值+=lowbit(x)时,都会有进位,并且
//进位后的新x值一定包含所有原来的x值,也就是说,这一步
//充分地向上进位,达到区间和更新的目的。
ll query(int x) //查询前缀和
{
int res = 0;
for(; x; x -= lowbit(x))
res += tree[x];
return res;
}
ll getsum(int x, int y) //求区间和
{
return query(y) - query(x - 1); //这里注意所求区间是包含了x的,所以是减去 x - 1。
}
int main()
{
cin >> n >> m;
build();
while(m --)
{
int k;
int x, y;
cin >> k >> x >> y;
if(k == 1)
updata(x, y);
else
cout << getsum(x,y) << endl;
}
return 0;
}
求逆序对(离散化)
首先要知道什么是逆序对:对于一个数组,如果i > j && a[i] < a[j]
,这两个数就算一对逆序对,简单来说,所有逆序对的个数和就是找每一个数的前面有几个比他的大的数,他们加起来的和就是逆序对的总数。
P1774 最接近神的人
我的理解:
离散化之后,每个点的值实际上就是它应该在的位置,比如:
100000 1000 10000 100 10 离散化之后就是:5 3 4 2 1。
我们可以遍历一遍离散化之后的数组,遍历到一个数之后,将树状数组里这个数应该在的位置(也就是离散化后的a【i】)上加一个1(也就是做一个标记,表示这个点已经放入了树状数组),比如遍历到5的话,就将树状数组里的5的位置上+1。
然后计算一下query(5),因为是query(5)是求的1 - 5的区间和,所以query(5)就是当前放入了树状数组里的所有比5小的数个数(包括5自己)。
设 i 是当前已经放了i个数进入树状数组,所以用i - qeury(a[i])就是当前所有比a[i]要大的数的个数,这就是a[i]的逆序对的个数,然后这样遍历一遍数组就可以得到所有的逆序对数。
所以query(5) = 1, i - query(5) = 1 - 1 = 0,ans += 0。
之后的过程如下图:
query(3) = 1, i - query(3) = 2 - 1 = 1, ans += 1。
query(4) = 2, i - query(4) = 3 - 2 = 1, ans += 1。
query(2) = 1,i - query(2)= 4 - 1 = 3, ans += 3。
query(1) = 1,i - query(1)= 5 - 1 = 4,ans += 4。
所以ans最终的结果是9。
代码:
#include<bits/stdc++.h>
#include<ext/rope>
#define ll long long
#define inf 0x3f3f3f3f
#define endl '\n'
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
using namespace std;
const int N = 6e5 + 10;
ll n, m, ans;
ll a[N], b[N], tree[N];
int lowbit(int x)
{
return x & (-x);
}
void updata(int x, int y) //单点更新
{
for(; x <= n; x += lowbit(x))
tree[x] += y;
}
ll query(int x) //查询前缀和
{
int res = 0;
for(; x; x -= lowbit(x))
res += tree[x];
return res;
}
bool cmp(int l,int r) //重载运算符用于离散化
{
return a[l] < a[r];
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
cin >> a[i], b[i] = i;
stable_sort(b + 1, b + n + 1, cmp);
for(int i = 1; i <= n; i ++)
a[b[i]] = i;
for(int i = 1; i <= n; i ++)
{
updata(a[i],1); // 标记a【i】
ans += i - query(a[i]);//到目前为止,一共往树状数组
//里放入了i个数,而query(a【i】)是树状数组中比a【i】
//小的数(因为是从左到右遍历的。),所以i - 树状数组中
//比a【i】小的数就是剩余的比a【i】大的数。
}
cout << ans << endl;
return 0;
}
其他题目
二维树状数组
#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define fi first
#define se second
#define endl '\n'
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
using namespace std;
const int N = (1 << 12) + 10;
int n, m, q;
int tree[N][N], t[N][N];
int lowbit(int x){return x&(-x);}
void add(int x, int y, int k)
{
for(int i = x; i <= n; i += lowbit(i))
for(int j = y; j <= n; j += lowbit(j))
tree[i][j] += k;
}
int query(int x, int y)
{
int res = 0;
for(int i = x; i; i -= lowbit(i))
for(int j = y; j; j -= lowbit(j))
res += tree[i][j];
return res;
}
int main()
{
cin >> n >> q;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
{
cin >> t[i][j];
if(t[i][j]) add(i, j, 1);
}
while(q --)
{
int p;
cin >> p;
if(p == 1)
{
int a, b;
cin >> a >> b;
if(t[a][b])
add(a, b, -1), t[a][b] --;
else
add(a, b, 1), t[a][b] ++;
}
else
{
int a, b, c, d;
cin >> a >> b >> c >> d;
int ans = query(c, d) - query(a - 1, d) - query(c, b - 1) + query(a - 1, b - 1);
cout << ans << endl;
}
}
return 0;
}
树状数组实现平衡树
(待补)