極簡版線段樹

来源:https://www.cnblogs.com/xiaojuA/archive/2018/09/08/xiaojudechujixianduanshu.html
-Advertisement-
Play Games

作者作為一個蒟蒻,也是最近才自學了線段樹,不對的地方歡迎大佬們評論,但是不要噴謝謝 好啦,我們就開始說說線段樹吧 線段樹是個支持區間操作和查詢的東東,平時的話還是蠻實用的 下麵以最基本的區間加以及查詢區間和為例 線段樹顧名思義就是棵樹嘛,葉子節點是每個基本點,它們所對應的父親就是它們的和,具體如下圖 ...


作者作為一個蒟蒻,也是最近才自學了線段樹,不對的地方歡迎大佬們評論,但是不要噴謝謝

好啦,我們就開始說說線段樹吧

線段樹是個支持區間操作和查詢的東東,平時的話還是蠻實用的

下麵以最基本的區間加以及查詢區間和為例

線段樹顧名思義就是棵樹嘛,葉子節點是每個基本點,它們所對應的父親就是它們的和,具體如下圖

3,4,5都是基本點

但是對於這樣的線段樹來說,操作所需的時間是遠達不到我們的要求的(會被t),因為我們會進行一些不必要的操作,就像如果沒有查詢到某個點,那麼就沒有必要去修改這個點的值,為此,我們會引入一個懶標記,記錄每個基本點需要被加上的值(稱為add),那麼樹上任意一個點需要增加的值=該點對應的區間長度*add

那麼總的來說,線段樹的基本操作我個人認為可以分成3個,建樹、修改和查詢,當然如果繼續細分也是口以(可以)的,就比如說還可以分出 區間和的向上傳遞(父親節點等於子節點的和)和懶標記的向下傳遞(子節點的懶標記=原來的懶標記+父節點的懶標記)

所以接下來我們就來看看建樹、修改和查詢這3部分的具體代碼吧(深呼吸)

首先是建樹(build)

#define ls 2*rt,l,(l+r)/2                //left son
#define rs 2*rt+1,(l+r)/2+1,r      //right son
#define ll long long
void build(ll rt,ll l,ll r)//rt是當前點,l和r代表l到r區間的和
{
    if(r==l)
//也就是說,我們找到了一個葉子節點,自己到自己的和 就是自己嘛
    {
        scanf("%lld",&su[rt]);//那我們就輸入這個節點的值
    }
    else//否則就去看看當前點的左右兒子
    {
        build(ls);//看左兒子
        build(rs);//看右兒子
        //當rt的左右兒子都準備好了,我們就可以求出rt的值了
        su[rt]=su[2*rt]+su[2*rt+1];
    }
    return;
} //一層一層的求,我們就可以建好一個初步的樹啦

然後是修改(change)

#define ls 2*rt,l,(l+r)/2          //左右兒子,和之前一樣
#define rs 2*rt+1,(l+r)/2+1,r
#define ll long long
void change(ll rt,ll l,ll r,ll L,ll R,ll add)
//當前點,當前區間的左右端點,需要修改的區間的左右端點,需要給每個基本點加上的值
{
    if(l>=L&&r<=R)//如果說當前區間是需要修改區間的子集
    {
        su[rt]+=add*(r-l+1);
        //那麼就修改當前點,註意乘上當前區間長度
        o[rt]+=add;
//記得修改懶標記 return;//別忘了返回! } if(o[rt]!=0) //如果說我們恰好經過了一個被打上懶標記的點,那不如就順手把它的懶標記下傳好了 { //修改左右兒子的值 su[rt*2]+=o[rt]*((r+l)/2-l+1);// (r+l)/2是區間中點 su[rt*2+1]+=o[rt]*(r-(r+l)/2);//實際應乘以(r-((r+l)/2+1)+1)但+1-1抵消了 o[rt*2]+=o[rt]; o[rt*2+1]+=o[rt]; //下傳懶標記註意是加上父節點的懶標記不是等於 o[rt]=0;//清除懶標記 } if(L<=(l+r)/2) //二分思想,如果需要修改的區間左端點在當前區間中點的左邊,即當前區間中點左側有需要修改的點的話 { change(ls,L,R,add);//那就去修改啊 } if(R>(l+r)/2)//同理 { change(rs,L,R,add); } su[rt]=su[2*rt]+su[2*rt+1];//橘氏春秋有雲(什麼鬼)有下就有上,改完記得上傳 return; }

呼啊,已經完成2/3了,堅持就是勝利!↖(^ω^)↗

查詢(find)

void find(ll rt,ll l,ll r,ll L,ll R)
//當前點,當前區間左右端點,需要查詢的區間左右端點
{
    if(l>=L&&r<=R)//如果當前區間是查詢區間的子集
    {
        ans[c]+=su[rt];//答案就加上當前點的值
    }
    else//不然就找找它應該在那個區間裡面
    {
        if(o[rt]!=0)//順便下傳rt的懶標記
        {
            su[rt*2]+=o[rt]*((r+l)/2-l+1);
            su[rt*2+1]+=o[rt]*(r-(r+l)/2);
            o[rt*2]+=o[rt];
            o[rt*2+1]+=o[rt];
            o[rt]=0;
        }
        if(L<=(l+r)/2)//二分思想,如果左邊有點
        {
            find(ls,L,R);//那就找找左邊
        }
        if(R>(l+r)/2)//如果右邊有點
        {
            find(rs,L,R);//那就找找右邊
        }
        su[rt]=su[2*rt]+su[2*rt+1];//還是那句老話,橘氏春秋有雲:有下就有上
    }
    return;//看到return我就開心↖(^ω^)↗
}

哇吼,結束了才怪,接下來是總代碼!

//線段樹要寫成lazy[i]+=lazy[祖先]的形式
//溫馨提示,炒雞重要,我這個蒟蒻就被坑了嚶嚶嚶
#include<iostream>
#include<cstdio>
#define ls 2*rt,l,(l+r)/2
#define rs 2*rt+1,(l+r)/2+1,r
#define ll long long
using namespace std;
int n,m,a,c;
ll su[400005],x,y,k,ans[100005],o[400005];//數組開4倍
void build(ll rt,ll l,ll r)
{
    if(r==l)
    {
        scanf("%lld",&su[rt]);
    }
    else
    {
        build(ls);
        build(rs);
        su[rt]=su[2*rt]+su[2*rt+1];
    }
    return;
}
void change(ll rt,ll l,ll r,ll L,ll R,ll add)
{
    if(l>=L&&r<=R)
    {
        su[rt]+=add*(r-l+1);
        o[rt]+=add;
        return;
    }
    if(o[rt]!=0)
    {
        su[rt*2]+=o[rt]*((r+l)/2-l+1);
        su[rt*2+1]+=o[rt]*(r-(r+l)/2);
        o[rt*2]+=o[rt];
        o[rt*2+1]+=o[rt];
        o[rt]=0;
    }
    if(L<=(l+r)/2)
    {
        change(ls,L,R,add);
    }
    if(R>(l+r)/2)
    {
        change(rs,L,R,add);
    }
    su[rt]=su[2*rt]+su[2*rt+1];
    return;
}
void find(ll rt,ll l,ll r,ll L,ll R)
{
    if(l>=L&&r<=R)
    {
        ans[c]+=su[rt];
    }
    else
    {
        if(o[rt]!=0)
        {
            su[rt*2]+=o[rt]*((r+l)/2-l+1);
            su[rt*2+1]+=o[rt]*(r-(r+l)/2);
            o[rt*2]+=o[rt];
            o[rt*2+1]+=o[rt];
            o[rt]=0;
        }
        if(L<=(l+r)/2)
        {
            find(ls,L,R);
        }
        if(R>(l+r)/2)
        {
            find(rs,L,R);
        }
        su[rt]=su[2*rt]+su[2*rt+1];
    }
    return;
}
int main()
{
    scanf("%d %d",&n,&m);//n個基本點,m次操作
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&a);
        if(a==1)//我們要進行區間加啦
        {
            scanf("%lld %lld %lld",&x,&y,&k);//在x到y上加k
            change(1,1,n,x,y,k);
//            for(int i=1;i<=2*n;i++)cout<<" "<<i<<"="<<su[i];
//            cout<<"\n";
//            寫給需要調試的小可愛的
        }
        if(a==2)//查詢
        {
            c++;//個人喜歡統一輸出,c記錄第幾次詢問
            scanf("%lld %lld",&x,&y);//查詢x到y的和
            find(1,1,n,x,y);
        }
    }
    for(int i=1;i<=c;i++)
    {
        printf("%lld\n",ans[i]);//統一輸出答案
    }
}

這樣,一棵完完整整的基礎簡化版線段樹就寫完了

有問題的話可以問呦~雖然我也不一定會但是我會儘力解答的!

感謝閱讀


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 好久沒寫博客了,小伙伴們最近在幹嘛呢? 最近在搞AI開放平臺,就類似騰訊優圖,百度人工智慧平臺~~. 說得是很高大上啦,核心技術的演算法並不是我寫的。我負責搞API介面,寫前端。 前端的Vue和Bootstrap,兩門技術是目前前端比較流利的。最近也在搞Vue,感覺挺吃力的~_~ 正文 pycharn ...
  • 在寫爬蟲的時候總是遇到一些以圖片的形式展示的信息,因此要怎麼解析圖片上的信息呢?在Google上查了一下,需要安裝pytesseract和pillow(我用的python3.7)和Tesseract-OCR 1. 安裝pytesseract pip insatll pytesseract 2. 安裝 ...
  • 以下代碼是單片機程式,51單片機,編譯器為HT-IDE3000, 簡單來說 頭文件中只能申明, 變數在頭文件中申明時,要加上extern 這個關鍵字用來告訴編譯器,變數在其它的文件中定義,為什麼要在頭文件中申明變數? >因為想在其它文件里的代碼中使用這些變數, 如在a.c中使用b.c里定義的變數, ...
  • 一.函數的定義 return語句不寫或後邊不加任何對象即為return None 二.函數的參數 無參數 一個參數 多個參數 必須參數 必須按照正確順序和數量傳入參數 關鍵字參數 預設參數 預設參數必須放在必須參數的後面 不定長參數 參數帶一個星號* 參數帶兩個星號** 定義函數的參數時請以必須參數 ...
  • 動態鏈接 要解決空間浪費和更新困難這兩個問題最簡單的辦法就是把程式的模塊相互分割開來,形成獨立的文件,而不再將它們靜態地鏈接在一起。簡單地講,就是不對那些組成程式的目標文件進行鏈接,等到程式要運行時才進行鏈接。也就是說,把鏈接這個過程推遲到了運行時再進行,這就是動態鏈接( Dynamic Linki ...
  • 過期重磅: 全國電腦等級考試二級 Python 語言程式設計考試大綱 (2018 年版) 考試內容 一、Python語言的基本語法元素 二、基本數據類型 三、程式控制結構 四、函數和代碼復用 五、組合數據類型 六、文件和數據格式化 七、Python計算生態 考試方式 上機考試,考試時長 120 分 ...
  • Redis是一個開源(BSD許可),記憶體存儲的數據結構伺服器,可用作資料庫,高速緩存和消息隊列代理。 有時,為了提升整個網站的性能,在開發時會將經常訪問的數據進行緩存,這樣在調用這個數據介面時,可以提高數據載入的效率 本文將在Boot項目中進行Redis的整合,將常用的數據緩存到Redis伺服器中, ...
  • eclipse安裝hibernate tools 下載地址: https://tools.jboss.org/downloads/jbosstools/photon/4.6.0.Final.html 線上安裝 離線安裝 新建java project後加入對應jar包 新建hibernate.cfg. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...