HDU 1166 敵兵布陣 樹狀陣列從0到1

2021-09-10 01:58:49 字數 3872 閱讀 9243

如果給你乙個陣列,讓你求某個區間的和,你很自然會想到遍歷一遍陣列,複雜度是o(n),但是如果有多次詢問呢,你也許會想到用字首陣列,通過o(n)的預處理,達到o(1)的查詢,但是如果要更新某個元素的值呢,如果用字首和的思想,每更新乙個元素就會更新字首陣列,每次複雜度是o(n),如果有n次更改,複雜度為o(n^2)。有沒有更快的呢,這時候樹狀陣列就排上用場了,樹狀陣列可以用來解決動態陣列連續和的問題。

樹狀陣列(binary indexed tree(b.i.t), fenwick tree):是乙個查詢和修改複雜度都為log(n)的資料結構。主要用於查詢任意兩位之間的所有元素之和,但是每次只能修改乙個元素的值;經過簡單修改可以在log(n)的複雜度下進行範圍修改,但是這時只能查詢其中乙個元素的值(如果加入多個輔助陣列則可以實現區間修改與區間查詢)。

樹狀陣列大致就長下面這個模樣。

那麼樹狀陣列到底是怎麼把修改和求和都做到o(log(n))呢 

當我麼求和的時候,c[1]=a[1],c[2]=c[1]+a[2],c[4]=c[2]+c[3]+a[4],當我們更新a[2]的時候,我們只需要更新c[2],c[4],c[8],所以相當與每層我們只操作乙個結點,即o(log(n))。

當我們更新的時候c[i]的時候,就從c[i]開始往右走,邊走邊往上爬。比如我們更新c[2]的時候,我們先更新c[2],然後更新c[4]、c[8]、c[16]。

當我們要求和的時候,我們從c[i]開始往左走,邊走邊往上爬。比如我們求前7項和,先找到c[7],然後加上c[6],最後加上c[4]。

那我們怎麼才能做到這種奇怪的走法呢,

圖中灰色結點表示c[i],白色結點表示以i結尾的和,很神奇,灰色結點c[4]=a[1]+a[2]+a[3]+a[4]表示以4結尾的和,我們很好理解,但是為什麼灰色結點c[6]表示的表示的只有a[5]+a[6] 我們怎麼來控制個數呢,對於c[4],我們相當於從下標4開始,往前數了4個數,對於c[6],我們從下標6開始往前數兩個數,有個公式c[i]=a[i-2^k+1]+a[i-2^k+2]......+a[i],其中k表示i的二進位制末尾0的個數

i2進製

k下標取值範圍

10001

0i-2^k+1...i=1...1

c[1]=a[1]

20010

1i-2^k+1...i=1...2

c[2]=a[1]+a[2]=c[1]+a[2]

30011

0i-2^k+1...i=3...3

c[3]=a[3]

40100

2i-2^k+1...i=1...4

c[4]=a[1]+a[2]+a[3]+a[4]=c[2]+c[3]+a[4]

50101

0i-2^k+1...i=5...5

c[5]=a[5]

60110

1i-2^k+1...i=5...6

c[6]=a[5]+a[6]=c[5]+a[6]

所以我們求和的時候我們就先找i,然後往前數2^k個數,既然k表示i的二進位制末尾0的個數,其實就是前一位對應的權值,比如4的二進位制為0100,末尾有2個0,所以2^k=4,從後往前數第一位不為0的出現在第三位,權值為100,即4。

那我們我們怎麼計算這個權值呢,lowbit(i)=2^k=x&-x,先給出**

int lowbit(int x)

還是以4為栗子:lowbit(4)=4,4二進位制為0100,-4的二進位制為4的二進位制按位取反然後加1,-4=1100,所以4&(-4)=0100 即為4;

所以c[i]等於以i開始從後往前數i&(-i)個數。

當我們更新c[i]的時候,就從c[i]開始往右走,邊走邊往上爬。所以i逐漸變大但小於等於n,即更新時的下標變化:i+=lowbit(i)

當我們要求和的時候,我們從c[i]開始往左走,邊走邊往上爬。所以i逐漸變小但大於0,即求和時下標變化:i-=lowbit(i)

預處理方法:先把a和c陣列都清空,然後執行n次add操作,所以時間複雜度為o(nlog(n))。

樹狀陣列主要是用來維護動態陣列連續和問題。

hdu 1166 敵兵布陣

題意:有n個營地,每個營地開始都有一定數量的敵軍,然後有下面三種命令

(1) add i j,i和j為正整數,表示第i個營地增加j個人(j不超過30)

(2)sub i j ,i和j為正整數,表示第i個營地減少j個人(j不超過30);

(3)query i j ,i和j為正整數,i<=j,表示詢問第i到第j個營地的總人數

題解:由於n=50000比較大,而且命令多達40000,所以可以用樹狀陣列來解決,當然也可以用線段樹來維護區間和,只不過線段樹**量比較大,所以對於這種單點更新求區間和問題最好使用樹狀陣列,寫起來節約時間,也不容易出錯。下面分別給出樹狀陣列和線段樹的**。

#include#includeusing namespace std;

const int maxn=50000+7;

int tree[maxn<<1]; //樹狀陣列

int n;

char s[10];

int lowbit(int x)

void add(int x,int d)

return ;

}int sum(int x)

return s;

}int main()

printf("case %d:\n",t);

int x,y;

while(scanf("%s",s)==1&&strcmp(s,"end"))

else if(!strcmp(s,"add"))

else }}

return 0;

}

#include#includeusing namespace std;

const int maxn =1e5+7;

long long int segtree[maxn<<2],a[maxn];

int n,x,y;

char s[10];

void pushup(int now) //維護的資訊 維護區間和

void build_tree(int l,int r,int now)

int mid=(l+r)>>1;

build_tree(l,mid,now<<1);

build_tree(mid+1,r,(now<<1)|1);

pushup(now);

}long long int query(int ql,int qr,int l,int r,int now)

long long int ans=0;

int mid=(l+r)>>1;

if(ql<=mid)ans+=query(ql,qr,l,mid,now<<1);

if(qr>mid)ans+=query(ql,qr,mid+1,r,(now<<1)|1);

return ans;

}void update(int pos ,int c,int l,int r,int now)

int mid=(l+r)>>1;

if(pos<=mid)update(pos,c,l,mid,now<<1);

else update(pos,c,mid+1,r,(now<<1)|1);

pushup(now);

}int main()

else if(!strcmp(s,"add"))

else }}

return 0;

}

HDU 1166 敵兵布陣 樹狀陣列

用樹狀陣列很簡單,太晚了,貼下 睡覺去。另,研究線段樹的時候,發現網上流傳著有幾種不同的線段樹,最正宗的是以單位區間為單位,只能處理線段 另外還有幾種葉子結點是點的,這種也可以用來處理點,所以這題是可以用這種線段樹做的。還搞不太清楚它們之間的關係。mark一下,明天再說。include includ...

HDU 1166 敵兵布陣 樹狀陣列

problem description c國的死對頭a國這段時間正在進行軍事演習,所以c國間諜頭子derek和他手下tidy又開始忙乎了。a國在海岸線沿直線布置了n個工兵營地,derek和tidy的任務就是要監視這些工兵營地的活動情況。由於採取了某種先進的監測手段,所以每個工兵營地的人數c國都掌握的...

HDU 1166 敵兵布陣 (樹狀陣列)

敵兵布陣 time limit 1000msmemory limit 32768kb64bit io format i64d i64u submit status description c國的死對頭a國這段時間正在進行軍事演習,所以c國間諜頭子derek和他手下tidy又開始忙乎了。a國在海岸線沿...