樹狀陣列與線段樹(二)
樹狀陣列
1.小朋友排隊
n 個小朋友站成一排。
現在要把他們按身高從低到高的順序排列,但是每次只能交換位置相鄰的兩個小朋友。
每個小朋友都有一個不高興的程度。
開始的時候,所有小朋友的不高興程度都是 0。
如果某個小朋友第一次被要求交換,則他的不高興程度增加 1,如果第二次要求他交換,則他的不高興程度增加 2(即不高興程度為 3),依次類推。當要求某個小朋友第 k 次交換時,他的不高興程度增加 k。
請問,要讓所有小朋友按從低到高排隊,他們的不高興程度之和最小是多少。
如果有兩個小朋友身高一樣,則他們誰站在誰前面是沒有關係的。
輸入格式
輸入的第一行包含一個整數 n,表示小朋友的個數。
第二行包含 nn 個整數 H1,H2,…,Hn,分別表示每個小朋友的身高。
輸出格式
輸出一行,包含一個整數,表示小朋友的不高興程度和的最小值。
資料範圍
1≤n≤100000
0≤Hi≤1000000
輸入樣例:
3
3 2 1
輸出樣例:
9
樣例解釋
首先交換身高為3和2的小朋友,再交換身高為3和1的小朋友,再交換身高為2和1的小朋友,每個小朋友的不高興程度都是3,總和為9。
解題思路:解題關鍵就是找到最少的交換次數,我們可能會想到氣泡排序法,他就是通過不斷的交換排序來實現的,因此我們可以大膽的假設一些規律,最少的交換次數==逆序對的個數。
對於氣泡排序來說,每次交換最多減少一個逆序數
假設陣列有k個逆序對,
①至少需要交換k次,因此次數>=k
②在氣泡排序中,每次交換(Ai,Ai+1)且Ai>Ai+1,因此必然會使逆序數減1
綜上我們得出最少的交換次數==逆序對的個數
接下來,我們要求每一個小朋友的逆序數,然後根據前n項和公式求出他的不高興數,在求解單個逆序數時,我們就用到樹狀陣列來統計在該陣列中,前方大於他的數和後方小於他的數,總和即為該數的逆序數。
程式碼:
#include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=1000010; int tr[N],h[N]; ll con[N]; int n; int lowbit(int x) { return x&-x; } void add(int x,int v) { for(int i=x;i<N;i+=lowbit(i)) tr[i]+=v; } int query(int x) { int ans=0; for(int i=x;i;i-=lowbit(i)) ans+=tr[i]; return ans; } int main() { int i,j; cin>>n; for(i=0;i<n;i++) { cin>>h[i]; h[i]++; } //計算前面比他大的數 long long ans=0; for(i=0;i<n;i++) { con[i]=query(N-1)-query(h[i]); add(h[i],1); } //計算後面比它小的數 memset(tr,0,sizeof(tr)); for(i=n-1;i>=0;i--) { con[i]+=query(h[i]-1); add(h[i],1); } //總和 for(i=0;i<n;i++) ans+=con[i]*(con[i]+1)/2; cout<<ans; return 0; }
差分
1.差分
輸入一個長度為n的整數序列。
接下來輸入m個操作,每個操作包含三個整數l, r, c,表示將序列中[l, r]之間的每個數加上c。
請你輸出進行完所有操作後的序列。
輸入格式
第一行包含兩個整數n和m。
第二行包含n個整數,表示整數序列。
接下來m行,每行包含三個整數l,r,c,表示一個操作。
輸出格式
共一行,包含n個整數,表示最終序列。
資料範圍
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整數序列中元素的值≤1000
解題思路:
給定a[1],a[2],...a[n]構造查分陣列b[N],使得
a[i]=b[1]+b[2]+...b[i]
核心操作:將a[L~R]全部加上C,等價於b[L]+=C,b[R+1]-=C
1.a[1~L-1]無影響
2.a[L~R]加上了C
3.a[R+1~N]無影響
#include<iostream> using namespace std; const int N=100010; int b[N],q[N]; void insert(int l,int r,int c) { b[l]+=c; b[r+1]-=c; } int main() { int n,i,j,m,l,r,c; cin>>n>>m; for(i=1;i<=n;i++) cin>>q[i]; for(i=1;i<=n;i++) b[i]=q[i]-q[i-1]; while(m--) { cin>>l>>r>>c; insert(l,r,c); } for(i=1;i<=n;i++) { q[i]=q[i-1]+b[i]; cout<<q[i]<<" "; } return 0; }
&n