數據結構與算法:線段樹(三):維護更多信息

前言

這次的題思維上倒不是很難,就是代碼量比較大。

一、開關

洛谷的這種板子題寫起來比cf順多了()

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll>pll;const int MAXN=1e5+5;//開著的燈數
vector<int>cnts(MAXN<<2);
//懶信息 -> 是否反轉
vector<bool>info(MAXN<<2);//懶更新
void lazy(int i,int n)
{cnts[i]=n-cnts[i];info[i]=!info[i];
}//下發
void down(int i,int ln,int rn)
{if(info[i]){lazy(i<<1,ln);lazy(i<<1|1,rn);info[i]=false;}
}//匯總
void up(int i)
{cnts[i]=cnts[i<<1]+cnts[i<<1|1];
}void build(int l,int r,int i)
{if(l==r){cnts[i]=0;}else{int m=(l+r)>>1;build(l,m,i<<1);build(m+1,r,i<<1|1);up(i);}info[i]=false;
}//范圍反轉
void reverse(int jobl,int jobr,int l,int r,int i)
{if(jobl<=l&&r<=jobr){lazy(i,r-l+1);}else{int m=(l+r)>>1;down(i,m-l+1,r-m);if(jobl<=m){reverse(jobl,jobr,l,m,i<<1);}if(m+1<=jobr){reverse(jobl,jobr,m+1,r,i<<1|1);}up(i);}
}//范圍查詢
int query(int jobl,int jobr,int l,int r,int i)
{if(jobl<=l&&r<=jobr){return cnts[i];}int m=(l+r)>>1;down(i,m-l+1,r-m);int ans=0;if(jobl<=m){ans+=query(jobl,jobr,l,m,i<<1);}if(m+1<=jobr){ans+=query(jobl,jobr,m+1,r,i<<1|1);}return ans;
}void solve()
{int n,m;cin>>n>>m;build(1,n,1);int l,r;int type;while(m--){cin>>type>>l>>r;if(type==0){reverse(l,r,1,n,1);}else{cout<<query(l,r,1,n,1)<<endl;}}
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve();    }return 0;
}

這個題的思路其實就很好想了,如果用1代表開,0代表關,那么范圍內開著的燈的數量其實就可以表示為范圍內的累加和。而對于改變燈的狀態的這個操作,那么每次只需要將范圍內的累加和更新為范圍上的總數減去之前的累加和即可。所以代碼只需要對懶更新的函數lazy稍微修改一下即可。

二、貪婪大陸

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll>pll;const int MAXN=1e5+5;//維護范圍內放地雷的開頭個數和結尾個數
//如果要求查詢[l,r]上的地雷種數
//就是[1,r]范圍上的開頭個數減去[1,l-1]上的結尾個數//放地雷的開頭位置的個數
int bg[MAXN<<2];
//放地雷的結尾位置的個數
int ed[MAXN<<2];//匯總
void up(int i)
{bg[i]=bg[i<<1]+bg[i<<1|1];ed[i]=ed[i<<1]+ed[i<<1|1];
}void build(int l,int r,int i)
{if(l<r){int m=(l+r)>>1;build(l,m,i<<1);build(m+1,r,i<<1|1);up(i);}bg[i]=0;ed[i]=0;
}//范圍放置
//jobt==0:在添加開頭
//jobt==1:在添加結尾
void put(int jobt,int jobi,int l,int r,int i)
{if(l==r){if(jobt==0){bg[i]++;}else{ed[i]++;}}else{int m=(l+r)>>1;if(jobi<=m){put(jobt,jobi,l,m,i<<1);}else{put(jobt,jobi,m+1,r,i<<1|1);}up(i);}}//范圍查詢
int query(int jobt,int jobl,int jobr,int l,int r,int i)
{if(jobl<=l&&r<=jobr){return jobt==0?bg[i]:ed[i];}int m=(l+r)>>1;int ans=0;if(jobl<=m){ans+=query(jobt,jobl,jobr,l,m,i<<1);}if(m+1<=jobr){ans+=query(jobt,jobl,jobr,m+1,r,i<<1|1);}return ans;
}void solve()
{int n,m;cin>>n>>m;build(1,n,1);int q,l,r;while(m--){cin>>q>>l>>r;if(q==1){put(0,l,1,n,1);put(1,r,1,n,1);}else{int b=query(0,1,r,1,n,1);//等于1的話就沒左側了int e=l==1?0:query(1,1,l-1,1,n,1);cout<<b-e<<endl;}}
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve();    }return 0;
}

這個題就需要一點轉化了。由于要求查詢的是范圍內放置的地雷種數,而線段樹是不能直接用來維護這個信息的。所以可以考慮使用前綴和的思想,用線段樹維護范圍內每次放置地雷的開頭個數和結尾個數,那么這個范圍[l,r]內的地雷種數就是[1,r]上地雷的開頭個數減去[1,l-1]上地雷的結尾個數。那么這兩個信息匯總時就是和累加和一樣處理即可,每次放地雷時就是分別去開頭位置和結尾位置進行單點修改即可,所以put函數還要帶著jobt參數表示這次修改的是開頭還是結尾。同理,query函數也要帶著jobt表示查詢的類型。

三、無聊的數列

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll>pll;const int MAXN=1e5+5;//對于在[l,r]上加等差數列
//差分數組中可以在[l]加首項,之后每個位置都加公差,最后[r+1]減末項//原數組
vector<int>a(MAXN);
//差分數組
vector<int>diff(MAXN);
//差分數組累加和
vector<ll>sum(MAXN<<2);
//懶信息
vector<ll>info(MAXN<<2);//懶更新
void lazy(int i,int v,int n)
{sum[i]+=v*n;info[i]+=v;
}//下發
void down(int i,int ln,int rn)
{if(info[i]!=0){lazy(i<<1,info[i],ln);lazy(i<<1|1,info[i],rn);info[i]=0;}
}//匯總
void up(int i)
{sum[i]=sum[i<<1]+sum[i<<1|1];
}void build(int l,int r,int i)
{if(l==r){sum[i]=diff[l];}else{int m=(l+r)>>1;build(l,m,i<<1);build(m+1,r,i<<1|1);up(i);}info[i]=0;
}//范圍修改
void add(int jobl,int jobr,ll jobv,int l,int r,int i)
{if(jobl<=l&&r<=jobr){lazy(i,jobv,r-l+1);}else{int m=(l+r)>>1;down(i,m-l+1,r-m);if(jobl<=m){add(jobl,jobr,jobv,l,m,i<<1);}if(m+1<=jobr){add(jobl,jobr,jobv,m+1,r,i<<1|1);}up(i);}
}//范圍查詢
ll query(int jobl,int jobr,int l,int r,int i)
{if(jobl<=l&&r<=jobr){return sum[i];}int m=(l+r)>>1;down(i,m-l+1,r-m);ll ans=0;if(jobl<=m){ans+=query(jobl,jobr,l,m,i<<1);}if(m+1<=jobr){ans+=query(jobl,jobr,m+1,r,i<<1|1);}return ans;
}void solve()
{int n,m;cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++){diff[i]=a[i]-a[i-1];}build(1,n,1);int op;while(m--){cin>>op;if(op==1){ll l,r,k,d;cin>>l>>r>>k>>d;//開頭加首項add(l,l,k,1,n,1);//中間加公差if(l+1<=r){add(l+1,r,d,1,n,1);   }//后一項減末項if(r+1<=n){add(r+1,r+1,-(k+d*(r-l)),1,n,1);}}else{int q;cin>>q;cout<<query(1,q,1,n,1)<<endl;}}
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve();    }return 0;
}

這個題因為只涉及單點查詢,又因為每次范圍增加是增加一個等差數列,所以可以考慮用線段樹去維護差分數組的累加和,那么單點查詢時只需要查[1,q]上的累加和即可。而對于在差分數組中增加等差數列的操作,通過觀察等差數列的差分數組可以想到,方法就是在l位置加上首項,之后的每個位置都加公差,最后在r+1的位置減去末項即可。這里記得等差數列末項的公式是首項加項數減一乘公差即可。

四、方差

死去的高中數學還在追我()

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll>pll;const int MAXN=1e5+5;//原數組
vector<double>a(MAXN);
//累加和
vector<double>sum(MAXN<<2);
//平方和
vector<double>square(MAXN<<2);
//懶信息
vector<double>info(MAXN<<2);//懶更新
void lazy(int i,double v,int n)
{square[i]+=n*v*v+2*v*sum[i];sum[i]+=v*n;info[i]+=v;
}//下發
void down(int i,int ln,int rn)
{if(info[i]!=0){lazy(i<<1,info[i],ln);lazy(i<<1|1,info[i],rn);info[i]=0;}
}//匯總
void up(int i)
{sum[i]=sum[i<<1]+sum[i<<1|1];square[i]=square[i<<1]+square[i<<1|1];
}void build(int l,int r,int i)
{if(l==r){sum[i]=a[l];square[i]=a[l]*a[l];}else{int m=(l+r)>>1;build(l,m,i<<1);build(m+1,r,i<<1|1);up(i);}info[i]=0;
}//范圍增加
void add(int jobl,int jobr,double jobv,int l,int r,int i)
{if(jobl<=l&&r<=jobr){lazy(i,jobv,r-l+1);}else{int m=(l+r)>>1;down(i,m-l+1,r-m);if(jobl<=m){add(jobl,jobr,jobv,l,m,i<<1);}if(m+1<=jobr){add(jobl,jobr,jobv,m+1,r,i<<1|1);}up(i);}
}//范圍查詢
double query(int jobl,int jobr,int l,int r,int i,vector<double>&sum)
{if(jobl<=l&&r<=jobr){return sum[i];}int m=(l+r)>>1;down(i,m-l+1,r-m);double ans=0;if(jobl<=m){ans+=query(jobl,jobr,l,m,i<<1,sum);}if(m+1<=jobr){ans+=query(jobl,jobr,m+1,r,i<<1|1,sum);}return ans;
}void solve()
{int n,m;cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];}build(1,n,1);int op;int x,y;double k;while(m--){cin>>op>>x>>y;;if(op==1){cin>>k;add(x,y,k,1,n,1);}else if(op==2){cout<<fixed<<setprecision(4)<<query(x,y,1,n,1,sum)/(y-x+1)<<endl;}else{double sq=query(x,y,1,n,1,square)/(y-x+1);double avg=query(x,y,1,n,1,sum)/(y-x+1);cout<<fixed<<setprecision(4)<<sq-avg*avg<<endl;}}
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve();    }return 0;
}

首先,平均數這個信息好辦,就是用線段樹維護累加和信息,每次查詢范圍累加和除以范圍個數即可。但方差這個信息肯定是沒辦法直接用線段樹維護的,那么就可以考慮對方差公式進行一下轉化。具體的轉化就是:

\frac{\sum (x-\bar{x})^{2}}{n}=\frac{\sum x^{2}-2\bar{x}\sum x+n\bar{x}}{n}=\frac{\sum x^{2}}{n}-2\bar{x}^{2}+\bar{x}^{2}=\frac{\sum x^{2}}{n}-\bar{x}^{2}

那么就可以考慮用線段樹去維護平方和這個信息,然后再通過加工就能得到方差了。

又因為對于范圍增加操作,范圍上的平方和有:

\sum (x+v)^{2}=\sum (x^{2}+2xv+v^{2})=\sum x^{2}+2v\sum x+nv^{2}

所以在lazy函數里,范圍平方和就是在原來的基礎上加上兩倍的v乘范圍累加和,再加上范圍個數乘以v的平方即可。

總結

線段樹的題目還是要注意轉化。

END

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/94973.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/94973.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/94973.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【LeetCode_27】移除元素

刷爆LeetCode系列LeetCode27題&#xff1a;github地址前言題目描述題目思路分析代碼實現算法代碼優化LeetCode27題&#xff1a; github地址 有夢想的電信狗 前言 本文用C實現LeetCode 第27題 題目描述 題目鏈接&#xff1a;https://leetcode.cn/problems/remove-element/ …

C++11語言(三)

一、引言上期我們介紹了C11的大部分特性。C11的初始化列表、auto關鍵字、右值引用、萬能引用、STL容器的的emplace函數。要補充的是右值引用是不能取地址的&#xff0c;我們程序員一定要遵守相關的語法。操作是未定義的很危險。二、 仿函數和函數指針我們先從仿函數的形…

性能優化三劍客:`memo`, `useCallback`, `useMemo` 詳解

性能優化三劍客&#xff1a;memo, useCallback, useMemo 詳解 作者&#xff1a;碼力無邊各位React性能調優師&#xff0c;歡迎來到《React奇妙之旅》的第十二站&#xff01;我是你們的伙伴碼力無邊。在之前的旅程中&#xff0c;我們已經掌握了如何構建功能豐富的組件&#xff0…

好用的電腦軟件、工具推薦和記錄

固態硬盤讀寫測試 AS SSD Benchmark https://gitee.com/qlexcel/common-resource-backup/blob/master/AS%20SSD%20Benchmark.exe 可以測試SSD的持續讀寫、4K隨機讀寫等性能。也可以測試HDD的性能。 操作非常簡單&#xff0c;點擊Start(開始)即可測試。 體積小&#xff0c;免安…

Spring Task快速上手

一. 介紹Spring Task 是Spring框架提供的任務調度工具&#xff0c;可以按照約定的時間自動執行某個代碼邏輯&#xff0c;無需依賴額外組件&#xff08;如 Quartz&#xff09;&#xff0c;配置簡單、使用便捷&#xff0c;適合處理周期性執行的任務&#xff08;如定時備份數據、定…

函數(2)

6.定義函數的終極絕殺思路&#xff1a;三個問題&#xff1a;1.我定義函數&#xff0c;是為了干什么事情 函數體、2.我干完這件事&#xff0c;需要什么才能完成 形參3.我干完了&#xff0c;調用處是否需要繼續使用 返回值類型需要繼續使用 必須寫不需要返回 void小程序#include …

BGP路由協議(一):基本概念

###BGP概述 BGP的版本&#xff1a; BGP-1 RFC1105BGP-2 RFC1163BGP-3 RFC1267BGP-4 RFC1771 1994年BGP-4 RFC4271 2006年 AS Autonomous System 自治系統&#xff1a;由一個單一的機構或者組織所管理的一系列IP網絡及其設備所構成的集合 根據工作范圍的不同&#xff0c;動態路…

mit6.031 2023spring 軟件構造 筆記 Testing

當你編碼時&#xff0c;目標是使程序正常工作。 但作為測試設計者&#xff0c;你希望讓它失敗。 這是一個微妙但重要的區別。 為什么軟件測試很難&#xff1f; 做不到十分詳盡&#xff1a;測試一個 32 位浮點乘法運算 。有 2^64 個測試用例&#xff01;隨機或統計測試效果差&am…

【Unity開發】Unity核心學習(三)

四、三維模型導入相關設置 1、Model模型頁簽&#xff08;1&#xff09;場景相關&#xff08;2&#xff09;網格相關&#xff08;3&#xff09;幾何體相關2、Rig操縱&#xff08;骨骼&#xff09;頁簽 &#xff08;1&#xff09;面板基礎信息&#xff08;i&#xff09;None&…

C#語言入門詳解(17)字段、屬性、索引器、常量

C#語言入門詳解&#xff08;17&#xff09;字段、屬性、索引器、常量前言一、字段 Field二、屬性三、索引器四、常量內容來自劉鐵猛C#語言入門詳解課程。 參考文檔&#xff1a;CSharp language specification 5.0 中文版 前言 類的成員是靜態成員 (static member) 或者實例成…

Total PDF Converter多功能 PDF 批量轉換工具,無水印 + 高效處理指南

在辦公場景中&#xff0c;PDF 格式的 “不可編輯性” 常成為效率瓶頸 —— 從提取文字到格式轉換&#xff0c;從批量處理到文檔加密&#xff0c;往往需要多款工具協同。Total PDF Converter 破解專業版作為一站式 PDF 解決方案&#xff0c;不僅支持 11 種主流格式轉換&#xff…

[Windows] WPS官宣 64位正式版(12.1.0.22525)全新發布!

[Windows] WPS官宣 64位正式版 鏈接&#xff1a;https://pan.xunlei.com/s/VOYepABmXVfXukzlPdp8SKruA1?pwdeqku# 自2024年5月&#xff0c;WPS 64位版本在WPS社區發布第一個內測體驗安裝包以來&#xff0c;在近一年多的時間里&#xff0c;經過超過3萬名WPS體驗者參與版本測試…

WinExec

函數原型&#xff1a; __drv_preferredFunction("CreateProcess","Deprecated. See MSDN for details") WINBASEAPI UINT WINAPI WinExec(__in LPCSTR lpCmdLine,__in UINT uCmdShow); preferred : 更好的 __drv_preferredFunction("CreateProcess…

基于GA遺傳優化的雙向LSTM融合多頭注意力(BiLSTM-MATT)時間序列預測算法matlab仿真

目錄 1.前言 2.算法運行效果圖預覽 3.算法運行軟件版本 4.部分核心程序 5.算法仿真參數 6.算法理論概述 7.參考文獻 8.算法完整程序工程 1.前言 時間序列預測是機器學習領域的重要任務&#xff0c;廣泛應用于氣象預報、金融走勢分析、工業設備故障預警等場景。傳統時間…

Multi-Head RAG: Solving Multi-Aspect Problems with LLMs

以下是對論文《Multi-Head RAG: Solving Multi-Aspect Problems with LLMs》的全面解析&#xff0c;從核心問題、方法創新到實驗驗證進行系統性闡述&#xff1a;??一、問題背景&#xff1a;傳統RAG的局限性??傳統檢索增強生成&#xff08;RAG&#xff09;在處理??多維度復…

Jenkins 全方位指南:安裝、配置、部署與實戰應用(含圖解)

一、Jenkins 安裝 1.1 系統要求 基礎環境&#xff1a;Java 8 或 Java 11&#xff08;推薦&#xff09;、至少 2GB 內存、10GB 以上磁盤空間 支持系統&#xff1a;Windows、Linux&#xff08;Ubuntu/CentOS&#xff09;、macOS 網絡端口&#xff1a;默認使用 8080 端口&…

以國產IoTDB為代表的主流時序數據庫架構與性能深度選型評測

> &#x1f4a1; 原創經驗總結&#xff0c;禁止AI洗稿&#xff01;轉載需授權 > 聲明&#xff1a;本文所有觀點均基于多個領域的真實項目落地經驗總結&#xff0c;數據說話&#xff0c;拒絕空談&#xff01; 目錄 引言&#xff1a;時序數據庫選型的“下半場” 一、維…

7.2elementplus的表單布局與模式

基礎表單<template><el-form ref"ruleFormRef" :model"form" :rules"rules" label-width"100px"><el-form-item label"用戶名" prop"username"><el-input v-model"form.username"…

PyTorch實戰(3)——PyTorch vs. TensorFlow詳解

PyTorch實戰&#xff08;3&#xff09;——PyTorch vs. TensorFlow詳解0. 前言1. 張量2. PyTorch 模塊2.1 torch.nn2.2 torch.optim2.3 torch.utils.data3. 使用 PyTorch 訓練神經網絡小結系列鏈接0. 前言 PyTorch 是一個基于 Torch 庫的 Python 機器學習庫&#xff0c;廣泛用…

在win服務器部署vue+springboot + Maven前端后端流程詳解,含ip端口講解

代碼打包與基本配置 首先配置一臺win系統服務器&#xff0c;開放你前端和后端運行的端口&#xff0c;如80和8080 前端打包 前端使用vue3&#xff0c;在打包前修改項目配置文件&#xff0c;我使用的是vite所以是vite.config.js。 import { defineConfig } from vite import …