P3372 【模板】线段树 1
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
#define lc p<<1 ////p*2
#define rc p<<1|1 ////p*2+1
const int N=100005;
typedef struct node{
int l,r,sum,tag;
}node;
node tr[4*N];
int arr[N];
int n,q;
void build(int p,int l,int r){
tr[p]={l,r,arr[l],0};
if(l==r) return;
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
tr[p].sum=tr[lc].sum+tr[rc].sum;
}
void pushup(int p){ ////孩子区间向父区间更新
tr[p].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int p){ ////父区间向孩子区间更新
if(tr[p].tag){
tr[lc].sum+=tr[p].tag*(tr[lc].r-tr[lc].l+1);
tr[rc].sum+=tr[p].tag*(tr[rc].r-tr[rc].l+1);
tr[lc].tag+=tr[p].tag;
tr[rc].tag+=tr[p].tag;
tr[p].tag=0;
}
}
void update(int p,int l,int r,int x){
if( l<=tr[p].l && r>=tr[p].r ){
tr[p].sum+=x*(tr[p].r-tr[p].l+1);
tr[p].tag+=x;
return;
}
pushdown(p); ////先下放懒标记
int mid=(tr[p].l+tr[p].r)>>1;
if(l<=mid) update(lc,l,r,x);
if(r>mid) update(rc,l,r,x);
pushup(p); ////
}
int query(int p,int l,int r){
if( l<=tr[p].l && r>=tr[p].r ) return tr[p].sum;
pushdown(p);
int res=0;
int mid=(tr[p].r+tr[p].l)>>1;
if(l<=mid) res+=query(lc,l,r);
if(r>mid) res+=query(rc,l,r);
return res;
}
void solve(){
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>arr[i];
build(1,1,n);
while(q--){
int op; cin>>op;
if(op==1){
int x,y,k; cin>>x>>y>>k;
update(1,x,y,k);
}
else{
int x,y; cin>>x>>y;
cout<<query(1,x,y)<<endl;
}
}
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
solve();
return 0;
}
总结:区间加,维护区间和,最最最典的板子,没啥好说的。
P2003 [CRCI 2008] PLATFORME 平板
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lc p<<1
#define rc p<<1|1
const int N=500;
typedef struct node{
int l,r,h,tag;
}node;
typedef struct myarr{
int h0,l0,r0;
}myarr;
bool cmp(myarr a,myarr b){
return a.h0<b.h0;
}
node tr[40000];
myarr arr[N];
void build(int p,int l,int r){
tr[p]={l,r,0,0};
if(l==r) return;
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void pushup(int p){
tr[p].h=max(tr[lc].h,tr[rc].h);
}
void pushdown(int p){
if(tr[p].tag) {
tr[lc].h = max(tr[lc].h, tr[p].tag);
tr[rc].h = max(tr[rc].h, tr[p].tag);
tr[lc].tag = max(tr[lc].tag, tr[p].tag);
tr[rc].tag = max(tr[rc].tag, tr[p].tag);
tr[p].tag = 0;
}
}
void update(int p,int x,int y,int k){
if( x<=tr[p].l&&tr[p].r<=y ){
tr[p].h=max(tr[p].h,k);
tr[p].tag=max(tr[p].tag,k);
return;
}
pushdown(p);
int mid=(tr[p].l+tr[p].r)>>1;
if(x<=mid) update(lc,x,y,k);
if(y>mid) update(rc,x,y,k);
pushup(p);
}
int query(int p,int x,int y){
if(x==tr[p].l&&y==tr[p].r) return tr[p].h;
pushdown(p);
int res=0;
int mid=(tr[p].l+tr[p].r)>>1;
if(x<=mid) res=max(res,query(lc,x,y));
if(y>mid) res=max(res,query(rc,x,y));
return res;
}
void solve(){
int n;
cin>>n;
int maxn=0;
for(int i=1;i<=n;i++){
cin>>arr[i].h0>>arr[i].l0>>arr[i].r0;
maxn=max(maxn,arr[i].r0);
}
build(1,1,maxn-1); ////转换为格子!!!
sort(arr+1,arr+n+1,cmp);
int ans=0;
for(int i=1;i<=n;i++){
ans+=arr[i].h0-query(1,arr[i].l0,arr[i].l0);
ans+=arr[i].h0-query(1,arr[i].r0-1,arr[i].r0-1);
update(1,arr[i].l0,arr[i].r0-1,arr[i].h0);
}
cout<<ans<<"\n";
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
solve();
return 0;
}
总结:承接第一个板子。最最简单的修改第一个模板。只需要改成维护区间最大值即可。然后这题最好转换为一个一个的格子来做..
P3373 【模板】线段树 2
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
#define lc p<<1
#define rc p<<1|1
typedef struct node{
int l,r,sum,tag1,tag2; ////tag1为加法懒标记,tag2为乘法懒标记
}node;
const int N=100005;
node tr[4*N];
int n,q,mod;
int arr[N];
void build(int p,int l,int r){
tr[p]={l,r,arr[l],0,1}; ////tag2初始化应该是1!!
if(l==r) return;
int mid=(l+r)>>1;
if(l<=mid) build(lc,l,mid);
if(r>mid) build(rc,mid+1,r);
tr[p].sum=(tr[lc].sum+tr[rc].sum)%mod;
}
void pushup(int p){
tr[p].sum=(tr[lc].sum+tr[rc].sum)%mod;
}
void pushdown(int p){
int tag1=tr[p].tag1,tag2=tr[p].tag2;
tr[lc].sum=(tr[lc].sum*tag2+tag1*(tr[lc].r-tr[lc].l+1))%mod; ////先乘后加!!
tr[rc].sum=(tr[rc].sum*tag2+tag1*(tr[rc].r-tr[rc].l+1))%mod;
tr[lc].tag1=tr[lc].tag1*tag2+tag1,tr[lc].tag2*=tag2; ////新的乘上的tag2会影响原本的tag1吗???---我觉得会
tr[lc].tag1%=mod,tr[lc].tag2%=mod;
tr[rc].tag1=(tr[rc].tag1*tag2)+tag1,tr[rc].tag2*=tag2;
tr[rc].tag1%=mod,tr[rc].tag2%=mod;
tr[p].tag1=0,tr[p].tag2=1; ////tag2初始值为1.
}
void update(int p,int l,int r,int x,int typ){
if( l<=tr[p].l && r>=tr[p].r ){
if(typ==1){ ////区间乘
tr[p].sum*=x,tr[p].sum%=mod;
tr[p].tag2*=x,tr[p].tag2%=mod;
tr[p].tag1*=x,tr[p].tag1%=mod; ////tag2会影响之前的tag1
////tr[p].tag1=tr[p].tag1*(tr[p].r-tr[p].l+1)*x%mod; ////处理tag2对tag1的影响
////不对..如果多次连续对tag2更新的话,tag1的值会多加了
return;
}
else{ ////区间加
tr[p].sum+=x*(tr[p].r-tr[p].l+1),tr[p].sum%=mod;
tr[p].tag1+=x;
return;
}
}
pushdown(p);
int mid=(tr[p].l+tr[p].r)>>1;
if(l<=mid) update(lc,l,r,x,typ);
if(r>mid) update(rc,l,r,x,typ);
pushup(p);
}
int query(int p,int l,int r){
if( l<=tr[p].l && r>=tr[p].r ){
return tr[p].sum;
}
int res=0;
pushdown(p);
int mid=(tr[p].l+tr[p].r)>>1;
if(l<=mid) res+=query(lc,l,r),res%=mod;
if(r>mid) res+=query(rc,l,r),res%=mod;
return res%mod;
}
////本题关键在于tag1和tag2...后加上的tag2会影响前面的tag1...
////解决方案:先乘后加
////例如:
////①操作使:tr[1].tag1+=2;
////②再操作使:tr[1].tag2*=3; ////实际上在这个时候,tag2是会影响tag1的,但是不会影响3操作加上的4.所以更新tag2的时候,可以同时更新tag1.
////③再操作使:tr[1].tag1+=4;
void solve(){
cin>>n>>q>>mod;
for(int i=1;i<=n;i++) cin>>arr[i];
build(1,1,n);
int op,x,y,k;
while(q--){
cin>>op;
if(op==1){ ////区间乘
cin>>x>>y>>k;
update(1,x,y,k,1);
}
else if(op==2){ ////区间加
cin>>x>>y>>k;
update(1,x,y,k,2);
}
else if(op==3){ ////查询
cin>>x>>y;
cout<<query(1,x,y)<<endl;
}
}
}
signed main() {
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
solve();
return 0;
}
总结:在模板1的基础上,增加了区间乘的操作。所以需要两个懒标记,tag1记录加法,tag2记录乘法。这里有个问题就是tag2标记会影响已有的tag1。所以需要稍稍变动一点。
update时,当增加tag2之后,需要更新当前tag1。剩下的问题在于pushdown,应该先下放tag2,再下放tag1,即"先乘后加"。而tag2对tag1的影响已经提前处理了。
to be continue...
标签:tag1,tag2,int,线段,tr,rc,lc From: https://www.cnblogs.com/ouhq/p/18211829