傳送門:
這個題目的區間更新有加法和乘法。
所以比裸的線段樹難一點點吧,也就僅僅是一點點。
既然存在兩個操作,所以我們就要維護兩個tag,乙個加法乙個乘法。
但是pushdown的時候這兩個tag怎麼pushdown呢?
乘法的優先順序顯然比加法高,所以我們在mul更新的時候要先pushdown,這是乙個要點。第二個要點就是,pushdown的時候,對於乘法tag,我們可以直接乘上父節點的tag,但是對於加法的,我們要怎麼辦呢? 我們要先把該結點的tag乘上乘法tag,然後再加上父節點的tag。為什麼要這樣做呢?因為乘法優先順序高的嘛,這樣就完事了。
看不懂的話,下面我就來推導一下
假設父節點是ax+b
我們要pushdown左兒子。
我們要乘乙個k然後加上c
就變成了k(ax+b)+c
變成了kax+kb+c
變成了(ka)x+(kb+c)
右邊的kb+c就變成了add[rt<<1]*mul[rt]+add[rt]。ka就是mul[rt<<1]*mul[rt]。
下面是每次都比分塊慢的線段樹**:
(分塊寫這種區間更新的,不如線段樹方便,我就沒寫分塊的**。)
#include using namespace std;
typedef long long ll;
const int maxn = 1e6+7;
ll a[maxn];
ll sum[maxn<<2],add[maxn<<2],mul[maxn<<2];
ll n,p;
void pushup(int rt)
void pushdown(int rt,int ln,int rn)
}void build(int rt,int l,int r)
int mid = (l+r)/2;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}void add(int x,int y,int l,int r,int rt,int v)
int mid = (l+r)/2;
pushdown(rt,mid-l+1,r-mid);
if(x<=mid)
if(y>mid)
pushup(rt);
}void mul(int x,int y,int l,int r,int rt,int v)
if(x<=mid)
if(y>mid)
pushup(rt);
}ll query(int x,int y,int l,int r,int rt)
int mid = (l+r)/2;
pushdown(rt,mid-l+1,r-mid);
ll ans = 0;
if(x<=mid)
if(y>mid)
return ans;
}int main()
build(1,1,n);
int m;
scanf("%d",&m);
for(int i=0;ielse if(t==2)
else
}return 0;
}
洛谷P3372 線段樹模板
線段樹講的很詳細的部落格 鏈結 includeusing namespace std typedef long long ll const int maxn 100005 ll dat maxn 儲存資料 ll tree maxn 2 儲存線段樹的陣列常開成資料的4倍大小 ll add maxn 2...
線段樹模板(洛谷P3372)
給定序列,支援區間加 求區間和。線段樹的基本思路 線段樹模板嘛,不懂得看題解第一,dalao講解超詳細的 includeusing namespace std define ll long long int maxn 1000001 unsigned ll n,m,a maxn ans maxn 2...
洛谷P3373 模板 線段樹2
這題有毒啊,敲了我一晚上加一早上,總算a了。由於有加和乘兩個操作,要用2個lazy陣列。核心難點就是2個lazy陣列會相互影響。因為乘影響加,加不影響乘,所以我們先算乘。include include include include include include include include i...