1. 程式人生 > 其它 >樹狀陣列(專題)

樹狀陣列(專題)

技術標籤:資料結構

目錄:

樹狀陣列

樹狀陣列是什麼?

(待補)

模板題

洛谷P3374

#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;
}

其他題目