线段树

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的影响已经提前处理了。

C-图腾_第二十届西南科技大学ACM程序设计竞赛(同步赛) (nowcoder.com)

#define lc p<<1
#define rc p<<1|1
typedef struct node{
    int l,r,sum;
    int tag;
}node;
const int N=300005;
node tr[4*N];
int n,m,q,init=0;
set<int> position;
unordered_map<int,int> totem;
void build(int p,int l,int r){
    tr[p]={l,r,init,-1};
    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!=-1){          ////不能以0为初始值
        tr[lc].sum=(tr[lc].r-tr[lc].l+1)*tr[p].tag;
        tr[rc].sum=(tr[rc].r-tr[rc].l+1)*tr[p].tag;
        tr[lc].tag=tr[p].tag;
        tr[rc].tag=tr[p].tag;
        tr[p].tag=-1;
    }
}
void update(int p,int l,int r,int x){
    if(tr[p].l>=l&&tr[p].r<=r){
        tr[p].sum=(tr[p].r-tr[p].l+1)*x;
        tr[p].tag=x;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(mid>=l) update(lc,l,r,x);
    if(mid<r) update(rc,l,r,x);
    pushup(p);
}
int query(int p,int l,int r){
    if(tr[p].l>=l&&tr[p].r<=r) return tr[p].sum;
    int res=0;
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(mid>=l) res+=query(lc,l,r);
    if(mid<r) res+=query(rc,l,r);
    return  res;
}
void process(int pos,int val){
    auto lesser=--position.lower_bound(pos);
    auto bigger=position.upper_bound(pos);
    int ll=*lesser,rr=*bigger;
    int numl=totem[ll],numr=totem[rr];
    if(ll+1<=pos-1) update(1,ll+1,pos-1,numl*val);
    if(pos+1<=rr-1) update(1,pos+1,rr-1,val*numr);
    update(1,pos,pos,0);
}
void solve(){               ////C---线段树
    cin>>n>>m>>q;
    vector<int> a;
    for(int i=0;i<m;i++){
        int x; cin>>x;
        a.emplace_back(x);
        position.emplace(x);
    }
    for(int i=0;i<m;i++){
        int x; cin>>x;
        totem[a[i]]=x;
    }
    init=totem[1]*totem[m];
    build(1,1,n);
    update(1,1,1,0),update(1,n,n,0);  ////init
    for(int i=1;i<m-1;i++){
        int pos=a[i],val=totem[a[i]];
        process(pos,val);
    }
    while(q--){
        int op,x,y; cin>>op>>x>>y;
        if(op==1){
            position.emplace(x),totem[x]=y;
            process(x,y);
        }
        else if(op==2) cout<<query(1,x,y)<<endl;
    }
}

 

to be continue...

 

posted @ 2024-05-24 23:33  osir  阅读(1)  评论(0编辑  收藏  举报