1. 程式人生 > 程式設計 >如何關閉 IDEA 自動更新

如何關閉 IDEA 自動更新

線段樹——基礎

1.引語

線段樹,可謂是好用的資料結構(廢話)

2.線段樹作用

1.區間加和
2.區間查詢
3.區間最大值,最小值。。。
4.忘了

3.線段樹概念:

儲存:

線段樹需要用結構體儲存

程式碼:

struct tree{
    int l,r;
    long long pre,add,num;
}t[4*maxn];
其中,sum代表該節點維護的值,l,r代表該節點維護的區間範圍,add為懶標記

建樹

一個節點node,它的左兒子的編號為2*node,右節點為2*node+1。
其中,樹的每個沒有子節點的節點就是放置陣列a的
而每個根節點都是放置其兩個子節點的某些特性
(如加和,最大值,最小值....)
樹狀陣列的建樹影象可以看成是這樣:

當左區間和右區間所劃定的範圍相同,
也就是其為子節點,
那麼就可以將a陣列中對應的數值賦值到pre中。下面是程式碼:
void build(int p,int l,int r)
{
    t[p].l=l,t[p].r=r;//設立p點所對應的區間範圍為l-r
    if(l==r)
    {
        t[p].pre=a[l];
        return;
    }
    int mid=l+r>>1;//取中點
    build(p*2,l,mid);//左子樹,編號為p*2,範圍為 l-mid
    build(p*2+1,mid+1,r);//右子樹,編號為p*2+1,範圍為mid+1—r
    t[p].pre+=t[p*2].pre+t[p*2+1].pre;//p點的值為左右兩個子書節點的值的和

}

區間查詢:

查詢的思想是這樣:假如說要求取1-3的加和
根據上面的圖來看,首先從根節點開始找,分成1-2,3-4
而此時我們發現,1-2正好包括在1-3中,那麼直接返回值,
接著,由於3-4不在範圍內,繼續往下找
分為3,4,3在範圍內,直接返回三。
運用到程式碼中長這樣:
long long ask(int p,int l,int r)
{
    if(l<=t[p].l&&r>=t[p].r)//在範圍內
        return t[p].pre;//直接返回值
    else if(tree[i].r<l||tree[i].l>r)
        return 0;//這個區間和目標沒有關係,即在目標之外,直接返回0
    int mid=t[p].l+t[p].r>>1;
    long long ans=0;
    if(l<=mid)
        ans+=ask(p*2,l,r);//如果這個區間的左兒子與目標區間有交集,那麼搜尋。
    if(r>mid)
        ans+=ask(p*2+1,l,r);//如果這個區間的右兒子與目標區間有交集,那麼搜尋。
    return ans;
}

單點修改

演算法的目標是在dis的位置上加上一個k

知道目標還不是有手就行嗎,哦,我沒手啊

所以從根節點開始,看這個dis是左兒子還是右兒子。
比較好理解,因此直接上程式碼:
int add(int p,int k,int dis)
{
    if(tree[p].l==tree[p].r)//如果這個區間被完全包括在目標區間裡面,那麼就直接新增賦值 
    {
        tree[p].pre+=k;
        return;
    }   
    if(dis<=tree[i*2].r)
        add(i*2,k,dis);//dis的區間判斷 
    else
        add(i*2+1,k,dis);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;//回溯時需要重新賦值 
}

區間修改

思路:如果把這個區間加上k,相當於在這個區間上塗上一個k的標記,單點查詢時就把標記加上即可
int add(int p,int l,int r,int k)
{
    if(tree[p].l>=l&&tree[p].r<=r)
    {//如果這個區間被完全包括在目標區間裡面,講這個區間標記k
        tree[p].num+=k;
        return 0;
    }
    if(tree[i*2].r>=l)//左子樹的右區間的邊界小於左邊界 
        add(i*2,l,r,k);
    if(tree[i*2+1].l<=r)
        add(i*2+1,l,r,k);//右子樹的左邊區間小於右邊界 
} 

單點查詢

dis去哪,把路徑上所有標價加起來就好
void search(int p,int dis)
{
    ans+=tree[p].num;//一路加起來
    if(tree[p].l==tree[p].r)
        return;
    if(dis<=tree[p*2].r)
        search(p*2,dis);
    if(dis>=tree[p*2+1].l)
        search(p*2+1,dis);
}

至此,線段樹基礎部分完結