1. 程式人生 > >線段樹 模板題

線段樹 模板題

線段樹

Problem Description
已知一個數列,你需要進行下面兩種操作:
1.將某區間每一個數加上x
2.求出某區間每一個數的和
Input
第一行包含兩個整數N、M,分別表示該數列數字的個數和操作的總個數。
第二行包含N個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。
接下來M行每行包含3或4個整數,表示一個操作,具體如下:
操作1: 格式:1 x y k 含義:將區間[x,y]內每個數加上k
操作2: 格式:2 x y 含義:輸出區間[x,y]內每個數的和
Output
包含若干行整數,即為所有操作2的結果。
Sample Input
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
Sample Output
11
8
20
Tip
時空限制:1000ms,128M
Data Constraint
對於30%的資料:N≤8,M≤10
對於70%的資料:N≤1000,M≤10000
對於100%的資料:N≤100000,M≤100000

剛剛看到本題,很顯然,我們可以先嚐試一下暴力,當然,如果分塊的話,我們也可以大大加快我們對本題的處理。
那麼我們就有非常簡單的暴力程式碼。

#include <cstdio>

#define LL long long 

using namespace std;

LL n,m;
LL a[1000001];

int main()
{
	scanf("%lld%lld",&n,&m);
	for (LL i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for (LL i=1;i<=m;i++)
	{
		LL k,x,y,j;
		scanf("%lld%lld%lld",&j,&x,&y);
		switch (j){
			case 1:
				scanf("%lld",&k);
				for (LL p=x;p<=y;p++)
					a[p]+=k;
				break;
			case 2:
				{
					LL ans=0;
					for (LL p=x;p<=y;p++)
						ans+=a[p];
					printf("%lld\n",ans);
				}
				break;
		}
	}
}

觀察本題,很顯然我們需要建立一棵線段樹,其每個節點都儲存一段區間的和, 並且我們可能需要一個布林函式來幫助我們快速修改一段區間的值。當然,如果題目只是一個值一個值修改的話,我們可以選擇樹狀陣列來更快地處理問題。
那麼,什麼是線段樹呢?

線段樹是一種二叉搜尋樹,與區間樹相似,它將一個區間劃分成一些單元區間,每個單元區間對應線段樹中的一個葉結點。

簡單來說,線段樹的建立,一般是將一個數組tree[],每一個編號i都算一個節點,那麼我們就可以得到任意一個線段樹的節點 tree[i],並且使用結構體的方式儲存。
這樣,我們的tree[i],就可以記錄下它儲存的區間和的左端點 left 和右端點 right,以及這段區間的和 sum。同時,線段樹之所以是線段樹,是因為它的祖先、兒子存在包含關係。
簡單來說,如果我已經有一個節點tree[i],那麼我們通過它所儲存的區間的中點 mid=(left+right)/2 ,那麼我可以再為這個節點 tree[i] 記錄兩個值 leftson , rightson ,表示它的左兒子和右兒子,那麼,左右兒子分別繼承父親節點的一部分割槽間和,為了儘量降低複雜度,我們選擇左右兒子分別繼承父親節點的一半區間,所得到的左兒子 tree[leftson] ,它的區間左端點仍為 left , 右端點變成了mid 。所以右兒子 tree[rightson],它的區間左端點則為 mid+1 ,右端點則是 right 。由此我們明白,左右兒子所記錄的區間和就是父親節點記錄的區間和,那麼我們就可以通過這一點,進行一個搜尋。
我們從根節點進行搜尋一段區間,判斷這段區間是繼承到了左兒子還是右兒子處,也可能是兩個兒子分別佔有一部分,然後分別進行搜尋,就可以得到答案。
也正是因為每個節點記錄的是一段區間和,所以我們修改一段區間的數的大小時,我們只需一個節點一個節點處理就可以了,當然,為了節約時間,我們可以引入變數 change 來記錄這個節點的所有兒子和它是否需要修改,然後在我們遍歷到存在change 的點時,我們只需要增加該節點的和,並將change繼承給它的各個兒子,就可以了。
所以,我們有如下程式碼。

#include <cstdio>
 
#define LL long long  
 
using namespace std;
 
LL n,m;
LL q[100010];
struct node{
    LL left,right,sum,change;
}v[270000];
 
void build(LL ,LL ,LL );
void change(LL ,LL ,LL ,LL );
LL find(LL ,LL ,LL );
 
int main()
{
    scanf("%lld%lld",&n,&m);
    for (LL i=1;i<=n;i++)
        scanf("%lld",&q[i]);
    build(1,1,n);
    for (LL i=1;i<=m;i++)
    {
        LL j,x,y,k;
        scanf("%lld%lld%lld",&j,&x,&y);
        if (j==1)
        {
            scanf("%lld",&k);
            change(1,x,y,k);
        }
        else
            printf("%lld\n",find(1,x,y));
    }
    return 0;
}
 
LL find(LL ro,LL le,LL ri)
{
    if (v[ro].change)
    {
        v[ro].sum+=v[ro].change*(v[ro].right-v[ro].left+1);
        if (v[ro].left<v[ro].right)
        {
            v[ro<<1].change+=v[ro].change;
            v[ro<<1|1].change+=v[ro].change;
        }
        v[ro].change=0;
    }
    if (le==v[ro].left&&ri==v[ro].right)
        return v[ro].sum;
    LL mid=(v[ro].left+v[ro].right)>>1;
    if (ri<=mid)
        return find(ro<<1,le,ri);
    if (le>mid)
        return find(ro<<1|1,le,ri);
    return find(ro<<1,le,mid)+find(ro<<1|1,mid+1,ri);
}
 
void change(LL ro,LL le,LL ri,LL ca)
{
    v[ro].sum+=(ri-le+1)*ca;
    if (le==v[ro].left&&ri==v[ro].right)
    {
        if (v[ro].left<v[ro].right)
        {
            v[ro<<1].change+=ca;
            v[ro<<1|1].change+=ca;
        }
        return ;
    }
    if (v[ro].change)
    {
        v[ro].sum+=v[ro].change*(v[ro].right-v[ro].left+1);
        v[ro<<1].change+=v[ro].change;
        v[ro<<1|1].change+=v[ro].change;
        v[ro].change=0;
    }
    LL mid=(v[ro].left+v[ro].right)>>1;
    if (ri<=mid)
    {
        change(ro<<1,le,ri,ca);
        return ;
    }
    if (le>mid)
    {
        change(ro<<1|1,le,ri,ca);
        return ;
    }
    change(ro<<1,le,mid,ca);
    change(ro<<1|1,mid+1,ri,ca);
    return ;
}
 
void build(LL ro,LL le,LL ri)
{
    v[ro].left=le;v[ro].right=ri;
    if (le<ri)
    {
        LL mid=(le+ri)>>1;
        build(ro<<1,le,mid);
        build(ro<<1|1,mid+1,ri);
        v[ro].sum=v[ro<<1].sum+v[ro<<1|1].sum;
    }
    else
        v[ro].sum=q[le];
    return ;
}