1. 程式人生 > >淺談——線段樹

淺談——線段樹

#include<cstdio>
using namespace std;
const int maxn = 100005;
int n,m,x,y,flag;
int l[4*maxn],r[4*maxn];//l[x]:編號為x的單元左端點的數的編號 ,r[x]:編號為x的單元右端點的數的編號  
long long lazy[8*maxn],sum[4*maxn];// sum[x]:編號為x的單元表示的所有數的和(其實只有一個數) 
                                   // lazy[x]:要對編號為x的單元所加的數的多少 
                                
void
build(int L,int R,int now)//建一個由單元組成的樹 { lazy[now] = 0;//初始化 l[now] = L;//開始時第一個單元(根單元)編號為一,左端點的數的編號是1 r[now] = R;//右端點的數的編號是數的總個數n if(L == R)//最底層的葉子節點只有一個數,左端點就等於右端點 { scanf("%lld",&sum[now]);//讀入一個數據 return; } int mid = (L+R)/2;//將數的總數分為兩半,二分的思想,類似於滿二杈樹 build(L,mid,now*2
);//建上一個單元的左側的子單元 build(mid+1,R,now*2+1);//建上一個單元的右側的子單元 sum[now] = sum[now*2] + sum[now*2+1];//求二杈樹中兩個子單元所對應的父親單元的值,一層層向上搭建 } long long query(int L,int R,int now)//對它進行加數的操作,改變其值 { sum[now] += lazy[now] * (r[now] - l[now] + 1);//改變所求區間的值,彌補缺陷 lazy[now*2] += lazy[now];//lazy的值下傳到其左側的子區間 lazy[now*2
+1] += lazy[now];//lazy的值下傳到其右側的子區間 lazy[now] = 0;//lazy的值清零,防止其二次訪問產生多次重複操作造成答案錯誤 if(l[now] == L && r[now] == R)return sum[now];//找到所需要的區間,返回其表示的數的和 int mid = (l[now]+r[now])/2;//將現在的區間一分為二,即找現在區間的子區間 if(R <= mid) return query(L,R,now*2);//如果所求區間的右端點的數的編號小於左側的子區間的右端點的數的編號,則所求區間被左側的子區間所覆蓋,開始尋找左側的子區間 else if(L >= mid+1) return query(L,R,now*2+1);//如果所求區間的左端點的數的編號小於右側的子區間的左端點的數的編號,則所求區間被右側的子區間所覆蓋,開始尋找右側的子區間 else return query(L,mid,now*2)+query(mid+1,R,now*2+1);//如果都不是的話則證明所求區間被分開了,這時應該分頭尋找,在左右兩個子區間內尋找並求和 } void modify(int L,int R,long long d,int now)//尋找所求的區間,可能是分開的兩個區間 { if(l[now] == L && r[now] == R)//找到所需要的區間 { lazy[now] += d;//標記對所需要的區間加上的數的多少,但所求區間的值未發生改變 return;//返回(如果是兩個分開的區間,則開始尋找另一個區間) } sum[now] += (long long)d*(R-L+1);//d*(R-L+1)所要求加的總值,比如對2~4加2,則總共要加上6),它所有的根區間的值都發生了變化 int mid = (l[now]+r[now])/2;//將現在的區間一分為二,即找現在區間的子區間 if(R <= mid) modify(L,R,d,now*2);//如果所求區間的右端點的數的編號小於左側的子區間的右端點的數的編號,則所求區間被左側的子區間所覆蓋,開始尋找左側的子區間 else if(L >= mid+1) modify(L,R,d,now*2+1);//如果所求區間的左端點的數的編號小於右側的子區間的左端點的數的編號,則所求區間被右側的子區間所覆蓋,開始尋找右側的子區間 else//如果都不是的話則證明所求區間被分開了,這時應該分頭尋找 { modify(L,mid,d,now*2);//開始尋找左側的子區間 modify(mid+1,R,d,now*2+1);//開始尋找右側的子區間 } } int main() { scanf("%d%d",&n,&m); build(1,n,1);//建一個由單元組成的樹 while(m) { m--;//總操作次數 scanf("%d",&flag);//判斷要讓你幹什麼,是加數還是求和 if(flag == 1)//是加數 { long long k; scanf("%d%d%lld",&x,&y,&k); modify(x,y,k,1);//開始加數 } if(flag == 2)//是求和 { scanf("%d%d",&x,&y); printf("%lld\n",query(x,y,1));//輸出求和 } } return 0;//結束程式 }


我知道你們都很強,應該不需要我講...

所以...看程式碼好咯!我的程式碼很詳細的!