樹狀陣列(專題)
技術標籤:資料結構
目錄:
樹狀陣列
樹狀陣列是什麼?
(待補)
模板題
#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;
}