標籤:style blog color os io for 2014 html
為什麼線段樹可以求逆序數?
給一個簡單的序列 9 5 3 他的逆序數是3
首先要求一個逆序數有兩種方式:可以從頭開始往後找比當前元素小的值,也可以從後往前找比當前元素大的值,有幾個逆序數就是幾。
線段樹就是應用從後往前找較大值得個數。(一邊更新一邊查)
當前個數是 n = 10
元素 9 5 3
9先加入線段樹,T【9】+=1;查從T【9】到T【10】比9大的值,沒有sum = 0;
5 加入線段樹,T【5】 += 1,查從T【5】到T【10】比5大的值,有一個9,sum +=1;
3 加入線段樹,T【3】 += 1,查從T【3】到T【10】比3大的值,有兩個9和5,sum +=2;
最終sum = 3;
若元素值不能確定,那麼首先講數列離散化,在從前往後枚舉,找後面比前面小的統計個數……
離散化:例如2 5 8 3 10 等價於 1 3 4 2 5,可以通過排序加小小處理解決。
例如:
元素 9 5 3 4 1 離散化後得到索引
索引 1 2 3 4 5
將元素升序排序
元素 1 3 4 5 9
索引 5 3 4 2 1
枚舉到第i個數,我們需要求出從1到i-1中有多少個比a[i]大的數,更新答案。
具體怎麼做呢?
每次枚舉完一個數之後,將這個數插入到線段樹裡(注意:插到與其對應的位置)。
舉個例子:3 2 4 1。則線段樹的變化應該為:t[3]+=1;t[2]+=1;t[4]+=1;t[1]+=1;
設x=a[i],這樣,在插入一個數X時,首先求一下t[x+1]~t[n]的和,這個和就是1~i-1中有多少個比a[i]大的數
因為求一個數列的逆序數可以從兩個
在你插a[i]時,前面已經有多少個比a[i]大的數
POJ 2299#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>using namespace std;#define MAX INT_MAX#define MIN INT_MIN#define LL __int64#define init(a) memset(a,0,sizeof(a))#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1const int maxn = 500010;using namespace std;int T[maxn<<2], index[maxn], num[maxn];int n;int cmp(const int i, const int j){ return num[i] < num[j];}void update( int l, int r,int rt, int num, int add){ T[rt] += add; if(r==l) return ; int m = (l+r) >> 1; if(num <= m) update(lson, num, add); else update(rson, num, add);}int query( int l, int r, int rt,int L, int R){ if(L<=l && r<=R) return T[rt]; int m = (l+r) >> 1; int ans = 0; if(L <= m) ans += query(lson, L, R); if(R > m) ans += query(rson, L, R); return ans;}int main(){ while(~scanf("%d", &n)) { if(n==0) break; init(index); for(int i=1;i<=n;i++) { scanf("%d", &num[i]); update(1, n,1, i, 1);//離散化 index[i] = i; } sort(index+1, index+n+1,cmp); //排序num,更新索引 LL sum = 0; for(int i=1;i<=n;i++) { sum += (query( 1, n,1, 1, index[i])-1); update(1, n,1, index[i], -1); } printf("%I64d\n", sum); } return 0;}
HDU 1394
題意:一組數 每次把最前面的元素放在最後產生新的序列,再統計逆序數,求所有逆序中最小值
有一個公式:先求出原數列的逆序數,sum
那麼每次新逆序數 = sum-a[i] + (n-a[i] - 1)
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>using namespace std;#define MAX INT_MAX#define MIN INT_MIN#define LL __int64#define init(a) memset(a,0,sizeof(a))#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1const int maxn = 500010;using namespace std;int T[maxn<<2], index[maxn], num[maxn];int n;int cmp(const int i, const int j){ return num[i] < num[j];}void update( int l, int r,int rt, int num, int add){ T[rt] += add; if(r==l) return ; int m = (l+r) >> 1; if(num <= m) update(lson, num, add); else update(rson, num, add);}int query( int l, int r, int rt,int L, int R){ if(L<=l && r<=R) return T[rt]; int m = (l+r) >> 1; int ans = 0; if(L <= m) ans += query(lson, L, R); if(R > m) ans += query(rson, L, R); return ans;}int main(){ while(~scanf("%d", &n)) { if(n==0) break; init(index); for(int i=1;i<=n;i++) { scanf("%d", &num[i]); update(1, n,1, i, 1); index[i] = i; } sort(index+1, index+n+1,cmp); LL sum = 0; for(int i=1;i<=n;i++) { sum += (query( 1, n,1, 1, index[i])-1); update(1, n,1, index[i], -1); } // printf("%I64d\n", sum); LL ans = sum; for(int i = 1;i<=n;i++) { sum = sum - num[i] + (n-num[i]- 1); ans = min(sum,ans); } cout<<ans<<endl; } return 0;}