這裡就是求區間覆蓋問題,如果你的區間完全覆蓋它的,那麼你就比他強壯。求出比自己強壯的牛數。
樹狀數組:
樹狀數組是一個查詢和修改複雜度都為log(n)的資料結構,假設數組a[1..n],
用lowbit函數維護了一個樹的結構
那麼查詢a[1]+...+a[n]的時間是log層級的,而且是一個線上的資料結構,
支援隨時修改某個元素的值,複雜度也為log層級。
來觀察這個圖:
令這棵樹的結點編號為C1,C2...Cn。令每個結點的值為這棵樹的值的總和,那麼容易發現:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
...
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16
這裡有一個有趣的性質:
設節點編號為x,那麼這個節點管轄的區間為2^k(其中k為x二進位末尾0的個數)個元素。因為這個區間最後一個元素必然為Ax,
所以很明顯:Cn = A(n – 2^k + 1) + ... + An
算這個2^k有一個快捷的辦法,定義一個函數如下即可:
int lowbit(int x){
return x&(x^(x–1));
}
當想要查詢一個SUM(n)(求a[n]的和),可以依據如下演算法即可:
step1: 令sum = 0,轉第二步;
step2: 假如n <= 0,演算法結束,返回sum值,否則sum = sum + Cn,轉第三步;
step3: 令n = n – lowbit(n),轉第二步。
可以看出,這個演算法就是將這一個個區間的和全部加起來,為什麼是效率是log(n)的呢?以下給出證明:
n = n – lowbit(n)這一步實際上等價於將n的二進位的最後一個1減去。而n的二進位裡最多有log(n)個1,所以查詢效率是log(n)的。
那麼修改呢,修改一個節點,必須修改其所有祖先,最壞情況下為修改第一個元素,最多有log(n)的祖先。
所以修改演算法如下(給某個結點i加上x):
step1: 當i > n時,演算法結束,否則轉第二步;
step2: Ci = Ci + x, i = i + lowbit(i)轉第一步。
i = i +lowbit(i)這個過程實際上也只是一個把末尾1補為0的過程。
對於數組求和來說樹狀數組簡直太快了!
代碼:
注意這裡下排序,按照s,e中的e從大到小,如果e相等-s從小到大排序。這樣,比自己強壯的只能是前面的,而數組數組只需要記錄起始點就可以了。注意區間相同的話就是實力相當不能計算在內。
#include <stdio.h>#include <stdlib.h>#include <string.h>#define nMax 100010#define Max(a,b) (a>b?a:b)#define Min(a,b) (a<b?a:b)struct COW{int s,e,id;}cow[nMax];int ans[nMax];int cnt[nMax];int maxN = -1;//比較函數,按照e從大到小,s從小到大int cmp(const void * a, const void * b){struct COW *c = (struct COW *)a;struct COW *d = (struct COW *)b;if (c->e == d->e){return c->s - d->s;}elsereturn d->e - c->e;}//樹狀數組的三個函數,一個是求x的最後一個1的位置,在某一位置增加一個數,求出num以前的所有數的和這三個函數int lowbit(int x){return x&(x^(x - 1));}void add(int pos){while (pos <= maxN + 1){ans[pos] ++;pos += lowbit(pos);}}int sum(int num){int sum = 0;while (num > 0){sum += ans[num];num -= lowbit(num);}return sum;}int main(){int n;while (scanf("%d", &n) && n){maxN = -1;for (int i = 1; i <= n; ++ i){scanf("%d %d", &cow[i].s, &cow[i].e);cow[i].id = i;maxN = Max(maxN, cow[i].e);}memset(ans, 0, sizeof(ans));qsort(cow + 1, n, sizeof(cow[0]), cmp);for (int i = 1; i <= n; ++ i){if (cow[i].s == cow[i - 1].s && cow[i].e == cow[i - 1].e)//相等的話,不計算在內{cnt[cow[i].id] = cnt[cow[i - 1].id];}else//否則可以求出覆蓋本區間的所有牛的個數,由於排序,只能在前面cnt[cow[i].id] = sum(cow[i].s + 1);add(cow[i].s + 1);//將本區間的起始點加入到樹狀數組中}for (int i = 1; i < n; ++ i){printf("%d ", cnt[i]);}printf("%d\n", cnt[n]);}return 0;}