線段樹(區間操作+lazy) 很好的模板以及心得體會
這裡只討論區間處理+lazy
acm比賽考察不會出裸的模板,所以必須深刻理解線段樹原理,各函式的目的。
#include <string.h> #include <algorithm> #include <stdio.h> #include <math.h> #include <queue> #define MAXN 100010 #define inf 0x3f3f3f3f using namespace std; struct node{ int l,r;//區間[l,r] int add;//區間的延時標記 int sum;//區間和 int mx; //區間最大值 int mn; //區間最小值 }tree[MAXN<<2];//一定要開到4倍多的空間 void pushup(int index){ tree[index].sum = tree[index<<1].sum+tree[index<<1|1].sum; tree[index].mx = max(tree[index<<1].mx,tree[index<<1|1].mx); tree[index].mn = min(tree[index<<1].mn,tree[index<<1|1].mn); } void pushdown(int index){ //說明該區間之前更新過 //要想更新該區間下面的子區間,就要把上次更新該區間的值向下更新 if(tree[index].add){ //替換原來的值 /* tree[index<<1].sum = (tree[index<<1].r-tree[index<<1].l+1)*tree[index].add; tree[index<<1|1].sum = (tree[index<<1|1].r-tree[index<<1|1].l+1)*tree[index].add; tree[index<<1].mx = tree[index].add; tree[index<<1|1].mx = tree[index].add; tree[index<<1].mn = tree[index].add; tree[index<<1|1].mn = tree[index].add; tree[index<<1].add = tree[index].add; tree[index<<1|1].add = tree[index].add; tree[index].add = 0;*/ //在原來的值的基礎上加上val tree[index<<1].sum += (tree[index<<1].r-tree[index<<1].l+1)*tree[index].add; tree[index<<1|1].sum +=(tree[index<<1|1].r-tree[index<<1|1].l+1)*tree[index].add; tree[index<<1].mx += tree[index].add; tree[index<<1|1].mx += tree[index].add; tree[index<<1].mn += tree[index].add; tree[index<<1|1].mn += tree[index].add; tree[index<<1].add += tree[index].add; tree[index<<1|1].add += tree[index].add; tree[index].add = 0; } } void build(int l,int r,int index){ tree[index].l = l; tree[index].r = r; tree[index].add = 0;//剛開始一定要清0 if(l == r){ scanf("%d",&tree[index].sum); tree[index].mn = tree[index].mx = tree[index].sum; return ; } int mid = (l+r)>>1; build(l,mid,index<<1); build(mid+1,r,index<<1|1); pushup(index); } void updata(int l,int r,int index,int val){ if(l <= tree[index].l && r >= tree[index].r){ /*把原來的值替換成val,因為該區間有tree[index].r-tree[index].l+1 個數,所以區間和 以及 最值為: */ /*tree[index].sum = (tree[index].r-tree[index].l+1)*val; tree[index].mn = val; tree[index].mx = val; tree[index].add = val;//延時標記*/ //在原來的值的基礎上加上val,因為該區間有tree[index].r-tree[index].l+1 //個數,所以區間和 以及 最值為: tree[index].sum += (tree[index].r-tree[index].l+1)*val; tree[index].mn += val; tree[index].mx += val; tree[index].add += val;//延時標記 return ; } pushdown(index); int mid = (tree[index].l+tree[index].r)>>1; if(l <= mid){ updata(l,r,index<<1,val); } if(r > mid){ updata(l,r,index<<1|1,val); } pushup(index); } int query(int l,int r,int index){ if(l <= tree[index].l && r >= tree[index].r){ //return tree[index].sum; return tree[index].mx; //return tree[index].mn; } pushdown(index); int mid = (tree[index].l+tree[index].r)>>1; int ans = 0; int Max = 0; int Min = inf; if(l <= mid){ ans += query(l,r,index<<1); Max = max(query(l,r,index<<1),Max); Min = min(query(l,r,index<<1),Min); } if(r > mid){ ans += query(l,r,index<<1|1); Max = max(query(l,r,index<<1|1),Max); Min = min(query(l,r,index<<1|1),Min); } //return ans; return Max; //return Min; } int main() { int n,m,q,x,y,z; while(~scanf("%d%d",&n,&m)){ build(1,n,1); while(m--){ scanf("%d",&q); if(q == 1){ cout<<"查詢:(x,y)"<<endl; scanf("%d %d",&x,&y); cout<<query(x,y,1)<<endl; } else{ cout<<"更新(x,y)為z:"<<endl; scanf("%d %d %d",&x,&y,&z); updata(x,y,1,z); for(int i = 1; i <= n; ++i){ printf("a[%d] = %d\n",i,query(i,i,1)); } } } } return 0; }
拿到問題,簡單數學化題目要求 , 比如說這題
題意:
兩種操作 1)訂房間 給定長度x ,求連續長度>=x 的區間起始位置, 儘量靠左 , 查詢到結果後把該段長度標記為不可用
2) 退房間 給定xy,把xy區間標記為可用
思路:
操作1 等價於兩部, 1 查詢到可用區間的起始位置, 2 更新該段區間為不可用
操作2 等價於更新該段區間為可用
分析 :
資料結構
我們要知道線段樹資料結構需要哪些內容, l,r,lazy (必備) 、v(區間[l,r]內連續最大區間,用於查詢時判斷是否進入這個區間)、llen,rlen(區間[l,r]的字首, 字尾長度。 舉例分析 ,假設n為6,所需x為4 . 則一開始進入[1,6]。分成兩個區間[1,3][4,6[ 。這個時候左子樹的字尾 + 右子樹的字首可以符合條件,我們就要把答左子樹的字尾起點就是我們要的案)
struct node
{
int l,r,v,lazy,llen,rlen;
void changeLen()
{
llen = rlen = v = lazy*(r-l+1);
}
}node[N<<2]; // 線段樹的空間大概是陣列空間的4倍;
changelen()的作用,lazy標記了區間[l,r]的可用性(整體),那麼[l,r]內的v,llen,rlen 就等於 區間長度*lazy。
通過lazy的改變,修改最長連續區間、左後綴、右字首的值
build 建樹
定位到葉子節點後,標記lazy的1(通過changelen函式,修改v,llen, rlen 為 1) 。
儲存資訊:node[numb].l = l , node[numb].r = r(必須的,線段樹的基本)
void build(int l,int r,int numb) // 線段樹的建立;
{
node[numb].lazy=1;
node[numb].l = l;
node[numb].r = r;
node[numb].changeLen();
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,numb<<1);
build(mid+1,r,numb<<1|1);
}
change 更新操作
更新操作模板的架構基本是這樣的
if (區間重合,即定位到目標區間)
{
lazy標記 // 節約時間,不向下走
return;
}
// 沒有定位到目標區間,那麼向下一層走
if(node[numb].lazy != -1) // 向下走之前,倘若節點上有lazy標誌,那麼先更新子樹資訊,才能向下走
pushDown(numb)
mid = (node[numb].l + node[numb].r) / 2;
if(l>mid) // 目標區間 在本節點的右子樹上
更新右邊
else if(r<=mid)
更新左邊
else{ // 左右各佔一部分,分別更新
更新左
更新右
}
pushUp(numb); // 更新完左右子樹,那麼本節點的資訊也要修改
本題中,我們要明白更新時,那些引數是需要改變的,
1)我們定位到目標區間,更新其lazy,馬上能得到該區間上的 v=llen = rlen因此changelen()
2)PushDown ,向下傳遞,那麼子節點上的 lazy 就該被更新為 父節點上的lazy (這裡注意,本題的lazy取值只有1可用,0不可用,-1沒有lazy , 其他題有的lazy暫存的是操作次數那麼應該累加,比如對區間做加法,那麼向下傳遞應該是node[rt<<1].lazy+=node[rt].lazy)。並且同時更新子節點上的v,llen,rlen
3)PushUp , 用左右節點的資訊更新父節點。逐個分析父節點的各個變數 ,
l r lazy :跟子樹無關 。
v: 修改為 左節點的v 和 右節點的v 以及 左節點字尾 + 右節點字首 三者的最大值
llen: 左節點的字首, 當左節點區間都是可用時,那麼還需加上右節點的字首 (兩個區間能連起來)
rlen: 右節點的字尾, 當右節點區間都是可用時,還需加上左節點的字尾
void PushDown(int numb) // 向下往左右兒子方向更新資料;
{
// 為了父節點的lazy 對左右子樹的影響,要思考lazy會影響左右子樹的什麼
// 本題,lazy肯定要向下傳遞,子樹再根據lazy ,修改自身變數
//(標記為0 相當於該區間完全被佔用,也就是llen rlen v = 0 )
// 最後 把父節點的 lazy 標記取消 變成-1
node[numb<<1].lazy=node[numb].lazy;
node[numb<<1|1].lazy=node[numb].lazy;
node[numb<<1].changeLen();
node[numb<<1|1].changeLen();
node[numb].lazy=-1; // 更新完了要清零;
}
void PushUp(int numb)
{
// 假設父節點是A ,左右子樹分別是BC
// pushUp時 關心BC,對A的影響
// 1) A的連續區間最大長 v =max( B.v,C.v ,B.rlen+C.llen)
// 2) A的llen = B.llen 。 且當 B整隻為1 A.len += C.llen
// 3) A的rlen = C.rlen 且當 C整隻為1 A.rlen += B.rlen
int tmp = max(node[numb<<1].v, node[numb<<1|1].v);
node[numb].v = max(tmp, node[numb<<1].rlen + node[numb<<1|1].llen);
// 更新根節點的 區間內最長區間值
node[numb].llen = node[numb<<1].llen;
node[numb].rlen = node[numb<<1|1].rlen;
if(node[numb<<1].v == node[numb<<1].r-node[numb<<1].l + 1)
node[numb].llen += node[numb<<1|1].llen;
if(node[numb<<1|1].v == node[numb<<1|1].r - node[numb<<1|1].l + 1)
node[numb].rlen += node[numb<<1].rlen ;
}
void change(int l,int r,int numb,int val) // 插入更新資料;
{
if(node[numb].l==l&&node[numb].r==r) // 如果區間完全重合,則不需要再往下更新了,先儲存起來,可以節約很多的時間(lazy思想)
{
node[numb].lazy=val;
node[numb].changeLen();
return;
}
if(node[numb].lazy!=-1) PushDown(numb); // 因為沒有找到完全重合的區間,所以要先更新下一層區間;
int mid=(node[numb].r+node[numb].l)>>1;
if(l>mid) change(l,r,numb<<1|1,val);
else if(r<=mid) change(l,r,numb<<1,val);
else{
change(l,mid,numb<<1,val);
change(mid+1,r,numb<<1|1,val);
}
PushUp(numb); // 最後還得往上返回,更新父節點區間;
}
query查詢
if 查詢到目標區間
返回節點上的資訊 // 有時候是最大值,有時候是和
retrurn;
if lazy
pushDown() // 向下找之前,有lazy標記必須先向下更新
mid = (node[numb].l + node[numb].r) / 2;
if(l>mid) // 進入右子樹
return 查詢右子樹
else if(r<=mid) //進入左子樹
return 查詢左子樹
else
{
// 左右子樹都佔一部分
// 返回值 需要就題論題,
int tmp1 = query(l,mid,numb<<1);
int tmp2 = query(mid+1,r,numb<<1|1);
// 求最大值 return max(tmp1,tmp2);
// 求和 return tmp1+tmp2;
// 不過本題有不一樣的需求
}
分析本題, 需求是找到 滿足區間的起始位置, 而且儘可能靠左。 那麼判斷條件就應是 v ,llen ,rlen
1、如果左兒子的v>=x那麼就返回query(左兒子)
2、如果左兒子的字尾+右兒子的字首>=x,直接返回左兒子的字尾開始的下標
3、若果右兒子的v>=x那麼就返回query(右兒子)
4、那就只能返回0了(憂桑~~>_<)
由此可見不同題目要求,判斷條件,返回條件也要順應著修改
int query(int l,int r,int numb, int len)
{
if(node[numb].l==node[numb].r&&len == 1){
return node[numb].l;
}
if(node[numb].lazy!=-1) PushDown(numb);
int mid=(node[numb].r+node[numb].l)>>1;
// 若左邊區間大於等於val 則去左邊查
// 若左邊的字尾 + 右邊的字首 >= val ,則返回左邊字尾的起點
// 若右邊區間大於等於val ,則去右邊
if(node[numb<<1].v >= len)
return query(l,mid,numb<<1,len);
if(node[numb<<1].rlen + node[numb<<1|1].llen >= len)
return node[numb<<1].r - node[numb<<1].rlen + 1;
if(node[numb<<1|1].v >= len)
return query(mid+1,r,numb<<1|1, len);
return 0;
}
完整程式碼
#include<iostream>
#include<string>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1000005;
int ans[N];
struct node
{
int l,r,v,lazy,llen,rlen;
void changeLen()
{
llen = rlen = v = lazy*(r-l+1);
}
}node[N<<2]; // 線段樹的空間大概是陣列空間的4倍;
void build(int l,int r,int numb) // 線段樹的建立;
{
node[numb].lazy=1;
node[numb].l = l;
node[numb].r = r;
node[numb].changeLen();
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,numb<<1);
build(mid+1,r,numb<<1|1);
}
void PushUp(int numb)
{
// 假設父節點是A ,左右子樹分別是BC
// pushUp時 關心BC,對A的影響
// 1) A的連續區間最大長 v =max( B.v,C.v ,B.rlen+C.llen)
// 2) A的llen = B.llen 。 且當 B整隻為1 A.len += C.llen
// 3) A的rlen = C.rlen 且當 C整隻為1 A.rlen += B.rlen
int tmp = max(node[numb<<1].v, node[numb<<1|1].v);
node[numb].v = max(tmp, node[numb<<1].rlen + node[numb<<1|1].llen);
// 更新根節點的 區間內最長區間值
node[numb].llen = node[numb<<1].llen;
node[numb].rlen = node[numb<<1|1].rlen;
if(node[numb<<1].v == node[numb<<1].r-node[numb<<1].l + 1)
node[numb].llen += node[numb<<1|1].llen;
if(node[numb<<1|1].v == node[numb<<1|1].r - node[numb<<1|1].l + 1)
node[numb].rlen += node[numb<<1].rlen ;
}
void PushDown(int numb) // 向下往左右兒子方向更新資料;
{
// 為了父節點的lazy 對左右子樹的影響,要思考lazy會影響左右子樹的什麼
// 本題,lazy肯定要向下傳遞,子樹再根據lazy ,修改自身變數
//(標記為0 相當於該區間完全被佔用,也就是llen rlen v = 0 )
// 最後 把父節點的 lazy 標記取消 變成-1
node[numb<<1].lazy=node[numb].lazy;
node[numb<<1|1].lazy=node[numb].lazy;
node[numb<<1].changeLen();
node[numb<<1|1].changeLen();
node[numb].lazy=-1; // 更新完了要清零;
}
void change(int l,int r,int numb,int val) // 插入更新資料;
{
if(node[numb].l==l&&node[numb].r==r) // 如果區間完全重合,則不需要再往下更新了,先儲存起來,可以節約很多的時間(lazy思想)
{
node[numb].lazy=val;
node[numb].changeLen();
return;
}
if(node[numb].lazy!=-1) PushDown(numb); // 因為沒有找到完全重合的區間,所以要先更新下一層區間;
int mid=(node[numb].r+node[numb].l)>>1;
if(l>mid) change(l,r,numb<<1|1,val);
else if(r<=mid) change(l,r,numb<<1,val);
else{
change(l,mid,numb<<1,val);
change(mid+1,r,numb<<1|1,val);
}
PushUp(numb); // 最後還得往上返回,更新父節點區間;
}
// 查詢到 連續區間>=len的 返回左邊第一個座標
int query(int l,int r,int numb, int len)
{
if(node[numb].l==node[numb].r&&len == 1){
return node[numb].l;
}
if(node[numb].lazy!=-1) PushDown(numb);
int mid=(node[numb].r+node[numb].l)>>1;
// 若左邊區間大於等於val 則去左邊查
// 若左邊的字尾 + 右邊的字首 >= val ,則返回左邊字尾的起點
// 若右邊區間大於等於val ,則去右邊
if(node[numb<<1].v >= len)
return query(l,mid,numb<<1,len);
if(node[numb<<1].rlen + node[numb<<1|1].llen >= len)
return node[numb<<1].r - node[numb<<1].rlen + 1;
if(node[numb<<1|1].v >= len)
return query(mid+1,r,numb<<1|1, len);
return 0;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--)
{
int x,y,z;
scanf("%d",&z);
if(z==1){
scanf("%d",&x),cout<<(y=query(1,n,1,x))<<endl;
if(y!=0)
change(y,y+x-1,1,0);
}
else
scanf("%d%d",&x,&y),change(x,x+y-1,1,1);
}
return 0;
}
1、分析資料結構
1、lazy: 加1操作時,如果該節點以下的子節點+1 不會產生貢獻, 那麼本次操作寄存在該節點上,即lazy+1
2、num: 該節點管轄的區間下,還差多少能產生貢獻度,如果能產生貢獻度,那麼必須更新到葉子節點上
3、v : 貢獻度
4、b: 分母b陣列
2、更新操作
當查詢到目標區間,對目標區間內的num--,
如果減完的num >0 ,那麼本次操作寄存到lazy 。
當num ==0 ,則表明產生了貢獻,那麼此時需要更新到葉子節點
pushUp 保證了父節點的num、v 受到左右子節點影響,到底層更新節點,回溯回去後就更新。
void Insert(int l,int r,int numb) // 插入更新資料;
{
if(node[numb].l==l&&node[numb].r==r) // 找到目標區間 ,那麼區間上的 num--, 因為這段區間是被更新的
{
node[numb].num --;
if(node[numb].num > 0)
{
// 還產生價值
node[numb].lazy ++ ;// 找到目標區間,而且未產生價值,那麼這段區間以下的 加操作先存到lazy裡。
return ;
}
else
{
if(node[numb].l == node[numb].r) // 當num == 0 , 則必須要更新到葉子節點
{
node[numb].v ++;
node[numb].num = node[numb].b;
node[numb].lazy = 0;
return;
}
}
}
if(node[numb].lazy)
PushDown(numb);
int mid=(node[numb].r+node[numb].l)>>1;
if(l>mid)
Insert(l,r,numb<<1|1);
else if(r<=mid)
Insert(l,r,numb<<1);
else
{
Insert(l,mid,numb<<1);
Insert(mid+1,r,numb<<1|1);
}
PushUp(numb);
}
完整程式碼
#include<iostream>
#include<string>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1000005;
int ans[N];
int b[N];
struct node
{
int l,r,v,lazy,b,num; // v: 值 ,b: 輸入的b陣列 num: 每個節點上 差多少能再得到一個滿足的 最小值
// lazy 標記為該節點以下 ,還得加多少次, 先寄存
} node[N<<2]; // 線段樹的空間大概是陣列空間的4倍;
void build(int l,int r,int numb) // 線段樹的建立;
{
node[numb].l=l;
node[numb].r=r;
node[numb].v=0;
node[numb].lazy=0; // 用了lazy思想,提高了效率;
if(l==r)
{
node[numb].b = b[l];
node[numb].num = b[l];
//cout<<b[l]<<' ';
return;
}
int mid=(l+r)>>1;
build(l,mid,numb<<1);
build(mid+1,r,numb<<1|1);
node[numb].num = min(node[numb<<1].num, node[numb<<1|1].num);
}
void PushUp(int numb)
{
node[numb].v= node[numb<<1].v + node[numb<<1|1].v;
node[numb].num = min(node[numb<<1].num,node[numb<<1|1].num );
}
void PushDown(int numb) // 向下往左右兒子方向更新資料;
{
node[numb<<1].lazy+=node[numb].lazy; // 寄存肯定是要 +=
node[numb<<1|1].lazy+=node[numb].lazy;
node[numb<<1].num-=node[numb].lazy;
node[numb<<1|1].num-=node[numb].lazy;
node[numb].lazy=0; // 更新完了要清零;
}
void Insert(int l,int r,int numb) // 插入更新資料;
{
if(node[numb].l==l&&node[numb].r==r) // 找到目標區間 ,那麼區間上的 num--, 因為這段區間是被更新的
{
node[numb].num --;
if(node[numb].num > 0)
{
// 還產生價值
node[numb].lazy ++ ;// 找到目標區間,而且未產生價值,那麼這段區間以下的 加操作先存到lazy裡。
return ;
}
else
{
if(node[numb].l == node[numb].r) // 當num == 0 , 則必須要更新到葉子節點
{
node[numb].v ++;
node[numb].num = node[numb].b;
node[numb].lazy = 0;
return;
}
}
}
if(node[numb].lazy)
PushDown(numb);
int mid=(node[numb].r+node[numb].l)>>1;
if(l>mid)
Insert(l,r,numb<<1|1);
else if(r<=mid)
Insert(l,r,numb<<1);
else
{
Insert(l,mid,numb<<1);
Insert(mid+1,r,numb<<1|1);
}
PushUp(numb);
}
int query(int l,int r,int numb)
{
if(node[numb].l==l&&node[numb].r==r)
{
return node[numb].v;
}
if(node[numb].lazy)
PushDown(numb);
int mid=(node[numb].r+node[numb].l)>>1;
if(l>mid)
return query(l,r,numb<<1|1);
else if(r<=mid)
return query(l,r,numb<<1);
else
{
return query(l,mid,numb<<1) + query(mid+1,r,numb<<1|1) ;
}
}
int main()
{
char str[10];
int x, y,n,m;
while(~scanf("%d%d", &n, &m))
{
for(int i=1; i<=n; i++)
scanf("%d",&b[i]);
build(1, n, 1);
for(int i = 0; i < m; i ++)
{
scanf("%s%d%d", str, &x, &y);
if(str[0] == 'a')
{
Insert(x, y, 1);
}
else
printf("%d\n", query(x, y, 1));
}
}
return 0;
}
/*
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<math.h>
#include<cstdlib>
#include<stdlib.h>
#include<queue>
#include<map>
#include<set>
#include<stack>
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define in1(a) scanf("%d" ,&a);
#define in2(a, b) scanf("%d%d", &a, &b);
#define in3(a, b, c) scanf("%d%d%d", &a, &b, &c);
#define out1(a) printf("%d\n", a);
#define out2(a, b) printf("%d %d\n", a, b);
#define pb(G, b) G.push_back(b);
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const int mod = 998244353;
const LL INF = 1e9+7;
const int N = 1010;
const double pi = 3.1415926;
int n, m, mi;
struct node
{
int l;
int r;
int num; //節點當前狀態,為0時意味著達到整除狀態
int lazy; //懶惰標記
int sum; //整除結果
int b; //b陣列初始值
}e[100010*4];
void build(int l, int r, int k)
{
e[k].l = l;
e[k].r = r;
e[k].sum = 0;
e[k].lazy = 0;
if(l == r) {
scanf("%d", &e[k].b);
e[k].sum = 0;
e[k].num = e[k].b;
return;
}
int mid = (l+r)/2;
build(l, mid, 2*k);
build(mid+1, r, 2*k+1);
e[k].num = min(e[2*k].num, e[2*k+1].num); //維護區間最小值
}
void push(int k)
{
if(e[k].lazy) {
e[2*k].num += e[k].lazy;
e[2*k+1].num += e[k].lazy;
e[2*k].lazy += e[k].lazy;
e[2*k+1].lazy += e[k].lazy;
e[k].lazy = 0;
}
}
void update(int l, int r, int k)
{
if(e[k].l == l && e[k].r == r) {
e[k].num --;
if(e[k].num) {
e[k].lazy --; //該區間沒有可以整除的節點
return;
}else {
if(e[k].l == e[k].r) { //找到達到整除狀態的節點
e[k].sum ++;
e[k].num = e[k].b; //更新
return;
}
}
}
push(k);
int mid = (e[k].l+e[k].r)/2;
if(r <= mid) update(l, r, 2*k);
else if(l > mid) update(l, r, 2*k+1);
else {
update(l, mid, 2*k);
update(mid+1, r, 2*k+1);
}
e[k].num = min(e[2*k].num, e[2*k+1].num); //維護區間最小值
e[k].sum = e[2*k].sum + e[2*k+1].sum;
}
int quary(int l, int r, int k)
{
if(e[k].l == l && e[k].r == r) {
return e[k].sum;
}
int mid = (e[k].l+e[k].r)/2;
if(r <= mid) return quary(l, r, 2*k);
else if(l > mid) return quary(l ,r, 2*k+1);
else {
return quary(l, mid, 2*k) + quary(mid+1, r, 2*k+1);
}
}
int main()
{
char str[10];
int x, y;
while(~scanf("%d%d", &n, &m)) {
build(1, n, 1);
for(int i = 0; i < m; i ++) {
scanf("%s%d%d", str, &x, &y);
if(str[0] == 'a') {
update(x, y, 1);
}else printf("%d\n", quary(x, y, 1));
}
}
return 0;
}
*/
手殘居然求和、最大值寫不出來,我恨!
#include<iostream>
#include<string>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1000005;
int ans[N];
struct node
{
int l,r,v,lazy,sum,maxx;
void changeLen()
{
sum += lazy*(r-l+1);
maxx += lazy;
}
} node[N<<2]; // 線段樹的空間大概是陣列空間的4倍;
void build(int l,int r,int numb) // 線段樹的建立;
{
node[numb].lazy=0;
node[numb].l = l;
node[numb].r = r;
node[numb].changeLen();
if(l==r)
return;
int mid=(l+r)>>1;
build(l,mid,numb<<1);
build(mid+1,r,numb<<1|1);
}
void PushUp(int numb)
{
node[numb].sum += node[numb<<1].sum + node[numb<<1|1].sum;
node[numb].maxx = max(node[numb<<1].maxx , node[numb<<1|1].maxx);
}
void PushDown(int numb) // 向下往左右兒子方向更新資料;
{
node[numb<<1].lazy=node[numb].lazy;
node[numb<<1|1].lazy=node[numb].lazy;
node[numb<<1].changeLen();
node[numb<<1|1].changeLen();
node[numb].lazy=0; // 更新完了要清零;
}
void change(int l,int r,int numb,int val) // 插入更新資料;
{
if(node[numb].l==l&&node[numb].r==r) // 如果區間完全重合,則不需要再往下更新了,先儲存起來,可以節約很多的時間(lazy思想)
{
node[numb].lazy=val;
node[numb].changeLen();
return;
}
if(node[numb].lazy!=0)
PushDown(numb); // 因為沒有找到完全重合的區間,所以要先更新下一層區間;
int mid=(node[numb].r+node[numb].l)>>1;
if(l>mid)
change(l,r,numb<<1|1,val);
else if(r<=mid)
change(l,r,numb<<1,val);
else
{
change(l,mid,numb<<1,val);
change(mid+1,r,numb<<1|1,val);
}
PushUp(numb); // 最後還得往上返回,更新父節點區間;
}
// 查詢到 連續區間>=len的 返回左邊第一個座標
int query(int l,int r,int numb)
{
if(node[numb].l==node[numb].r)
{
return node[numb].sum;
}
if(node[numb].lazy!=0)
PushDown(numb);
int mid=(node[numb].r+node[numb].l)>>1;
if(r <= mid)
return query(l,r,numb<<1);
else if(l > mid)
return query(l,r,numb<<1|1);
else
{
return query(l,mid,numb<<1) + query(mid+1, r, numb<<1|1);
}
}
int queryMax(int l,int r, int numb)
{
if(node[numb].l==node[numb].r)
{
return node[numb].maxx;
}
if(node[numb].lazy!=0)
PushDown(numb);
int mid=(node[numb].r+node[numb].l)>>1;
if(r <= mid)
return queryMax(l,r,numb<<1);
else if(l > mid)
return queryMax(l,r,numb<<1|1);
else
{
return max( queryMax(l,mid,numb<<1) , queryMax(mid+1, r, numb<<1|1));
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--)
{
int x,y,z;
scanf("%d",&z);
if(z==1)
{
scanf("%d%d",&x,&y);
cout<<query(x,y,1)<<endl;
}
else if(z == 2)
{
scanf("%d%d",&x,&y);
cout<<queryMax(x,y,1)<<endl;
}
else
scanf("%d%d",&x,&y),change(x,y,1,1);
}
return 0;
}