日常膜拜dalao :財神萬歲!!!!!!!!!!!!!!!!
話說這個線段樹今天折磨了我五個小時然後終於發現少打了乙個2.。。。
(離開學還有4天然而作業一字未動絲毫不慌 ing)
原題連線:
洛谷線段樹模板2
要求:如題,已知乙個數列,你需要進行下面三種操作:
1.將某區間每乙個數乘上x
2.將某區間每乙個數加上x
3.求出某區間每乙個數的和
輸入格式
第一行包含三個整數n、m、p,分別表示該數列數字的個數、操作的總個數和模數。
第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。
接下來m行每行包含3或4個整數,表示乙個操作,具體如下:
操作1: 格式:1 x y k 含義:將區間[x,y]內每個數乘上k
操作2: 格式:2 x y k 含義:將區間[x,y]內每個數加上k
操作3: 格式:3 x y 含義:輸出區間[x,y]內每個數的和對p取模所得的結果
樣例:輸入:
5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4
輸出:17
2查詢複雜度nlogn,修改複雜度採用懶標記可以優化到logn。
建樹:按自己的需求可以開結構體(然而我並沒用結構體,然後同於建二叉樹的操作(遞迴),先建左兒子和右兒子,再建自己
voidbt(
int now,
int l,
int r)
else
tree[now]
%=p;
return
;}
區間長度+k
使用add陣列,標記了add[i] +=k , 在計算長度時將i 號節點的標記計算進去就可以了,這樣避免了對每乙個節點的修改
區間長度* k
同上,陣列名為add2 .
注意事項
無論是加還是乘,修改方向都是自上而下的。
乘法優先,即規定好
兒子的值=此刻兒子的值乘爸爸的乘法lazytag+兒子的區間長度乘爸爸的加法lazytag
(ak+bkck+…+nk)=k(a+b+c+…+n)所以此刻兒子值乘 k相當於它的每乙個下面的都乘以k,然後再加上加法的值。
然而如果你加法優先的話,乘法和加法會黏成一團。。
在每次change兒子前先進行一次下放操作,把父親節點的標記都清掉,移到兒子節點上,並對兒子節點的值進行更新。
(話說我為啥不說單點修改呢?因為這其實就是對於區間長度為n的操作的一部分
( n ==1))
下放與更新操作
void
pushdown
(int now,
int l,
int r)
//下放
void
change
(int x,
int y,
int k,
int l,
int r,
int now)
//乘法
pushdown
(now,l,r)
;int mid=
(l+r)
>>1;
change
(x,y,k,l,mid,now*2)
;change
(x,y,k,mid+
1,r,now*2+
1); tree[now]
=(tree[now*2]
+tree[now*2+
1])%p;
}void
change2
(int x,
int y,
int k,
int l,
int r,
int now)
//加法
pushdown
(now,l,r)
;int mid=
(l+r)
>>1;
change2
(x,y,k,l,mid,now*2)
;change2
(x,y,k,mid+
1,r,now*2+
1); tree[now]
=(tree[now*2]
+tree[now*2+
1])%p;
return
;}
查詢操作
這個貌似沒啥好講的,,就是當前區間如果全在查詢區間內就累計。。
long
long
ask(
int askl,
int askr,
int now,
int l,
int r)
pushdown
(now,l,r)
;int mid=
(l+r)
>>1;
return
(ask
(askl,askr,now*
2,l,mid)
+ask
(askl,askr,now*2+
1,mid+
1,r)
)%p;
}
#include
//線段樹 洛谷p3373模板
#include
#include
#include
#define maxn 1000001
using namespace std;
int n,m,p;
long
long a[maxn]
,temp=0;
long
long tree[
2*maxn]
,add[
2*maxn]
,add2[
2*maxn]
;voidbt(
int now,
int l,
int r)
else
tree[now]
%=p;
return;}
void
pushdown
(int now,
int l,
int r)
long
long
ask(
int askl,
int askr,
int now,
int l,
int r)
pushdown
(now,l,r)
;int mid=
(l+r)
>>1;
return
(ask
(askl,askr,now*
2,l,mid)
+ask
(askl,askr,now*2+
1,mid+
1,r)
)%p;
}void
change
(int x,
int y,
int k,
int l,
int r,
int now)
pushdown
(now,l,r)
;int mid=
(l+r)
>>1;
change
(x,y,k,l,mid,now*2)
;change
(x,y,k,mid+
1,r,now*2+
1); tree[now]
=(tree[now*2]
+tree[now*2+
1])%p;
}void
change2
(int x,
int y,
int k,
int l,
int r,
int now)
pushdown
(now,l,r)
;int mid=
(l+r)
>>1;
change2
(x,y,k,l,mid,now*2)
;change2
(x,y,k,mid+
1,r,now*2+
1); tree[now]
=(tree[now*2]
+tree[now*2+
1])%p;
return;}
inline
long
long
read()
return s;
}int
main()
if(b==2)
//jia
if(b==3)
}return0;
}
洛谷P3373 模板 線段樹2
這題有毒啊,敲了我一晚上加一早上,總算a了。由於有加和乘兩個操作,要用2個lazy陣列。核心難點就是2個lazy陣列會相互影響。因為乘影響加,加不影響乘,所以我們先算乘。include include include include include include include include i...
洛谷 P3373 模板 線段樹 2
如題,已知乙個數列,你需要進行下面三種操作 1.將某區間每乙個數乘上x 2.將某區間每乙個數加上x 3.求出某區間每乙個數的和 第一行包含三個整數n m p,分別表示該數列數字的個數 操作的總個數和模數。第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。接下來m行每行包含3或4個...
洛谷 P3373 模板 線段樹 2
洛谷 p3373 模板 線段樹 2 已知乙個數列,你需要進行下面三種操作 1.將某區間每乙個數乘上x 2.將某區間每乙個數加上x 3.求出某區間每乙個數的和 思路 兩個lazy標記 add和mul,怎麼下放?我遇到的問題 很多題解上來就說先乘後加比先加後乘好處理。我疑惑 如果有兩個lazy標記,不知...