线段树cc

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;
    }
}

思路:二分下标,找到需要修改的区间。用线段树进行区间修改。

码题集OJ-小度的01串 (matiji.net)

思路:思路参考该题评论区。线段树维护的w0,w1,tag,分别是该区间以0开头需要的代价为w0,该区间以1开头需要的代价为w1,以及该区间被修改的次数tag。

“如果要用线段树维护区间信息,那么该信息应该满足区间可加性(也就是根节点的信息可以用左右子节点的信息计算出来),而差异度w0和w1满足这个性质。因为我们维护的序列是有序序列,即父节点维护的序列一定是左子树的序列接上右子树序列(不会发生翻转),因此,父节点的与01串和10串的差异度,其实就是两个儿子的差异度相加。”

“但是,父节点的信息并不是两个儿子的差异度简单相加,如果左子树的长度为奇数,那么不管将他改成01串还是10串,他的首尾都会是相同的,因此,如果要将右节点的序列接在左节点的序列后面,就要以不同于左儿子尾部字符的字符开头,例如,如果左儿子长为101,那么右儿子一定要以0开头,此时父节点的w1应该等于左节点的w1加上右节点的w0。”

“同时,我们还需要维护一个翻转序列的操作,对于一个01序列,如果将他翻转的话,我们与01串的差异度和10串的差异度刚好就会发生交换,因为所有的1变成了0,0变成了1。”用tag维护区间被修改的次数,pushdown时判断tag奇偶性决定是否要翻转。

#define lc p<<1
#define rc p<<1|1
const int N=300005;
typedef struct node{
    int l,r,w0,w1,tag;
}node;
node tr[4*N];
int n,q;
string str;
void build(int p,int l,int r){
    tr[p]={l,r,0,0,0};
    if(l==r){
        char c=str[l-1];
        if(c=='1') tr[p].w0=1;
        if(c=='0') tr[p].w1=1;
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    if( (tr[lc].r-tr[lc].l+1)&1 ){              根据上一个区间的奇偶拼接。
        tr[p].w0=tr[lc].w0+tr[rc].w1;
        tr[p].w1=tr[lc].w1+tr[rc].w0;
    }
    else{
        tr[p].w0=tr[lc].w0+tr[rc].w0;
        tr[p].w1=tr[lc].w1+tr[rc].w1;
    }
//    tr[p].w0=tr[lc].w0+tr[rc].w0;
//    tr[p].w1=tr[lc].w1+tr[rc].w1;
}
void pushup(int p){                     根据上一个区间的奇偶拼接。
    if( (tr[lc].r-tr[lc].l+1)&1 ){
        tr[p].w0=tr[lc].w0+tr[rc].w1;
        tr[p].w1=tr[lc].w1+tr[rc].w0;
    }
    else{
        tr[p].w0=tr[lc].w0+tr[rc].w0;
        tr[p].w1=tr[lc].w1+tr[rc].w1;
    }
//    tr[p].w0=tr[lc].w0+tr[rc].w0;
//    tr[p].w1=tr[lc].w1+tr[rc].w1;
}
void pushdown(int p){
    if(tr[p].tag&1){
        swap(tr[lc].w0,tr[lc].w1);
        swap(tr[rc].w0,tr[rc].w1);
        tr[lc].tag+=tr[p].tag;
        tr[rc].tag+=tr[p].tag;
        tr[p].tag=0;
    }
}
void update(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r){
        swap(tr[p].w0,tr[p].w1);
        tr[p].tag++;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) update(lc,l,r);
    if(r>mid) update(rc,l,r);
    pushup(p);
}
pair<int,int> query(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r){
        if( !((tr[p].l-l)&1) || tr[p].l==l ) return {tr[p].w0,tr[p].w1};     根据上一个区间的奇偶拼接。
        else return {tr[p].w1,tr[p].w0};
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    pair<int,int> res={0,0},ask;
    if(l<=mid) {
        ask=query(lc,l,r);
        res.first+=ask.first;
        res.second+=ask.second;
    }
    if(r>mid) {
        ask=query(rc,l,r);
        res.first+=ask.first;
        res.second+=ask.second;
    }
    return res;
}
void solve(){           BD202413--小度的01串-(王者)-线段树
    cin>>n>>q;
    cin>>str;
    build(1,1,n);
    while(q--){
        int op,l,r; cin>>op>>l>>r;
        if(op==1) update(1,l,r);
        if(op==2){
            pair<int,int> ans=query(1,l,r);
            cout<<min(ans.first,ans.second)<<endl;
        }
    }
}

5.龙骑士军团【算法赛】 - 蓝桥云课 (lanqiao.cn)

const int N=200005;
typedef struct node{
    int l,r,pre,suf,sum;
}node;
node tr[4*N];
int n,q;
int arr[N];
void pushup(int p){
    这个拼接是不对的。。
//    if(tr[lc].pre==tr[lc].sum&&tr[rc].pre>0) tr[p].pre=tr[lc].sum+tr[rc].pre;     另一边大于0才拼接
//    else tr[p].pre=tr[lc].pre;
//    if(tr[rc].suf==tr[rc].sum&&tr[lc].suf>0) tr[p].suf=tr[rc].sum+tr[lc].suf;     另一边大于0才拼接
//    else tr[p].suf=tr[rc].suf;
    tr[p].pre=max(tr[lc].pre,tr[lc].sum+tr[rc].pre);
    tr[p].suf=max(tr[rc].suf,tr[rc].sum+tr[lc].suf);
    tr[p].sum=tr[lc].sum+tr[rc].sum;
}
void build(int p,int l,int r){
    tr[p]={l,r,0,0,0};
    if(l==r){
        tr[p].pre=arr[l];
        tr[p].suf=arr[l];
        tr[p].sum=arr[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(p);
}
pair<int,int> queryL(int p,int l,int r){     找ab区间的suf
    if(l<=tr[p].l&&r>=tr[p].r) return {tr[p].suf,tr[p].sum};
    int mid=(tr[p].l+tr[p].r)>>1;
    pair<int,int> askL,askR;
    if(l>mid) return queryL(rc,l,r);        全在左区间
    if(r<=mid) return queryL(lc,l,r);       全在右区间
    askL=queryL(lc,l,r),askR=queryL(rc,l,r);
    return {max(askR.first,askR.second+askL.first),askL.second+askR.second};        merge
}
pair<int,int> queryR(int p,int l,int r){     找bc区间的pre
    if(l<=tr[p].l&&r>=tr[p].r) return {tr[p].pre,tr[p].sum};
    int mid=(tr[p].l+tr[p].r)>>1;
    pair<int,int> askL,askR;
    if(l>mid) return queryR(rc,l,r);
    if(r<=mid) return queryR(lc,l,r);
    askL=queryR(lc,l,r),askR=queryR(rc,l,r);
    return {max(askL.first,askL.second+askR.first),askL.second+askR.second};second是askL.second+askR.second
}
int querySum(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r) return tr[p].sum;
    int mid=(tr[p].l+tr[p].r)>>1;
    int res=0;
    if(l<=mid) res+=querySum(lc,l,r);
    if(r>mid) res+=querySum(rc,l,r);
    return res;
}
龙骑士军团-----merge!  法一:维护区间的pre和suf.
https://www.lanqiao.cn/problems/19680/learning/?contest_id=193
void solve(){
    cin>>n>>q;
    for(int i=1;i<=n;i++) cin>>arr[i];
    build(1,1,n);
    while(q--){
        int a,b,c,d; cin>>a>>b>>c>>d;
        if(b+1<=c-1) cout<<queryL(1,a,b).first+querySum(1,b+1,c-1)+queryR(1,c,d).first<<endl;
        else cout<<queryL(1,a,b).first+queryR(1,c,d).first<<endl;
    }
}
const int N=200005;
typedef struct node{
    int p,l,r,premin,premax;
}node;
node tr[4*N];
int n,q;
int arr[N];
void pushup(int p){
    tr[p].premin=min(tr[lc].premin,tr[rc].premin);
    tr[p].premax=max(tr[lc].premax,tr[rc].premax);
}
void build(int p,int l,int r){
    tr[p]={p,l,r,LONG_LONG_MAX,LONG_LONG_MIN};
    if(l==r){
        tr[p].premin=arr[l];
        tr[p].premax=arr[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(p);
}
int queryMin(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r) return tr[p].premin;
    int res=LONG_LONG_MAX;              开longlong!!!
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) res=min(res,queryMin(lc,l,r));
    if(r>mid) res=min(res,queryMin(rc,l,r));
    return res;
}
int queryMax(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r) return tr[p].premax;
    int res=LONG_LONG_MIN;              开longlong!!!
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) res=max(res,queryMax(lc,l,r));
    if(r>mid) res=max(res,queryMax(rc,l,r));
    return res;
}
龙骑士军团----法二:维护区间中前缀和的premin和premax.premax-premin即为查询结果,这个方法更好实现--wa了一发res没取LONG_LONG
法三--st表,区间最值查询
https://www.lanqiao.cn/problems/19680/learning/?contest_id=193
void solve(){
    cin>>n>>q;
    for(int i=1;i<=n;i++) {
        cin>>arr[i];
        arr[i]+=arr[i-1];    用前缀和来建树!
    }
    build(1,0,n);           细节:0-n
    while(q--){
        int a,b,c,d; cin>>a>>b>>c>>d;
        cout<<queryMax(1,c,d)-queryMin(1,a-1,b-1)<<endl;  选到点a-1,意味着a,b区间全取;最多减到b-1是因为至少选一个
    }
}
//自敲的测试用例
//22 46 91 91 65 77 77 79 91--expect
20 9
1 -5 3 4 -9 11 13 -10 1 16 -20 34 20 14 -15 10 5 9 -8 11
1 3 5 8
13 16 18 20
5 9 11 20
3 9 14 20
3 8 9 13
7 12 15 18
8 13 16 19
6 11 13 17
5 16 17 20

总结:维护区间左端,右端最大的连续子段和。

P1083 [NOIP2012 提高组] 借教室 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

const int N=1000006;
typedef struct node{
    int p,l,r,minn,tag;
}node;
node tr[4*N];
int arr[N];
void pushup(int p){
    tr[p].minn=min(tr[lc].minn,tr[rc].minn);
}
void pushdown(int p){
    if(tr[p].tag){
        tr[lc].minn-=tr[p].tag;
        tr[rc].minn-=tr[p].tag;
        tr[lc].tag+=tr[p].tag;
        tr[rc].tag+=tr[p].tag;
        tr[p].tag=0;
    }
}
void build(int p,int l,int r){
    tr[p]={p,l,r,INT_MAX,0};
    if(l==r){
        tr[p].minn=arr[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(p);
}
void update(int p,int l,int r,int x){
    if(l<=tr[p].l&&r>=tr[p].r){
        tr[p].minn-=x;
        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].minn;
    pushdown(p);            第一发少了pushdown
    int mid=(tr[p].l+tr[p].r)>>1;
    int res=INT_MAX;
    if(l<=mid) res=min(res,query(lc,l,r));
    if(r>mid) res=min(res,query(rc,l,r));
    return res;
}
int n,m;
借教室----区间修改,维护区间最小值--easy
https://www.luogu.com.cn/problem/P1083
void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>arr[i];
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int need,l,r; cin>>need>>l>>r;
        if(need<=query(1,l,r)) update(1,l,r,need);
        else{
            cout<<"-1"<<endl<<i;
            return;
        }
    }
    cout<<"0";
}

总结:区间维护最小值。

P1972 [SDOI2009] HH的项链 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

const int N=1000006;
typedef struct node{
    int p,l,r,sum,tag;
}node;
node tr[4*N];
int n,m;
int a[N],mark[N],ans[N];
typedef struct myp{
    int l,r,idx0;
    bool operator < (const myp &x) const{
        return r<x.r;
    }
}myp;
myp b[N];
void build(int p,int l,int r){
    tr[p]={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].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int p){
    int tag=tr[p].tag;
    if(tag){
        tr[lc].sum-=tag;
        tr[rc].sum-=tag;
        tr[lc].tag+=tag;
        tr[rc].tag+=tag;
        tr[p].tag=0;
    }
}
void add(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r){
        tr[p].sum++;
        tr[p].tag++;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) add(lc,l,r);
    if(r>mid) add(rc,l,r);
    pushup(p);
}
void delt(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r){
        tr[p].sum--;
        tr[p].tag--;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) delt(lc,l,r);
    if(r>mid) delt(rc,l,r);
    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 mid=(tr[p].l+tr[p].r)>>1;
    int res=0;
    if(l<=mid) res+=query(lc,l,r);
    if(r>mid) res+=query(rc,l,r);
    return res;
}
HH的项链--巧妙思维+线段树
思路:来自题解的一句话--对于若干个询问的区间[l,r],如果他们的r都相等的话,
key:那么项链中出现的同一个数字,一定是只关心出现在最右边的那一个的.--这就是这题的关键思维.
因为这个性质,那么就需要离线处理。把询问存下来,按r从小到大处理,就可以利用上面这条性质了。
对于样例1,2,3,4,3,5;
当i==5时,3出现了重复,那么此时,第一个3已经是没有意义的了,因为r是递增的,
如果询问的[l,r]中的l能包含第一个3,那么也必然能包含第二个3.但是3只能被算作一次。
多次重复的数字也是一样处理,擦去前一个的贡献,记录当前这个的贡献。---单点修改
https://www.luogu.com.cn/problem/P1972
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    build(1,1,n);
    cin>>m;
    for(int i=1;i<=m;i++) {
        cin>>b[i].l>>b[i].r;
        b[i].idx0=i;
    }
    sort(b+1,b+m+1);
    int idx=1;
    for(int i=1;i<=n;i++){
        if(!mark[a[i]]){
            mark[a[i]]=i;
            add(1,i,i);             单点修改,实际上树状数组更好维护
        }
        else{
            while(idx<=m&&b[idx].r<i){
                ans[b[idx].idx0]=query(1,b[idx].l,b[idx].r);
                idx++;
            }
            delt(1,mark[a[i]],mark[a[i]]);           单点修改
            add(1,i,i);
            mark[a[i]]=i;
        }
    }
    while(idx<=m){
        ans[b[idx].idx0]=query(1,b[idx].l,b[idx].r);
        idx++;
    }
    for(int i=1;i<=m;i++) cout<<ans[i]<<endl;
}

总结:下一题的铺垫题。离线处理。详细见注解。

​​​​​​P4113 [HEOI2012] 采花 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#define lowbit(x) (x)&(-x)
const int N=2000006;
int c[N];
int n,k,m;  k似乎没用--的确没用
int a[N],ans[N];
pair<int,int> mark[N];
typedef struct myp{
    int l,r,idx0;
    bool operator < (const myp &x) const{
        return r<x.r;
    }
}myp;
myp b[N];
void add(int pos){          单点修改
    for(int i=pos;i<=n;i+=lowbit(i)){
        c[i]+=1;
    }
}
void delt(int pos){         单点修改
    for(int i=pos;i<=n;i+=lowbit(i)){
        c[i]-=1;
    }
}
int query(int pos){
    int sum=0;
    for(int i=pos;i;i-=lowbit(i)){
        sum+=c[i];
    }
    return sum;
}
同上一题(https://www.luogu.com.cn/problem/P1972)一样,离线处理,读入查询之后,按r从小到大排序,依次处理.
key:上一题只关心最后一个出现的数字位置. 这一题只关心最后两个同一个数字出现的位置.上一题完全是这题的铺垫.
对于样例:1 2 2 2 3 1;
当i==3和i==4时会出现重复.
当i==3时,应该把上一次出现的2的位置add.
当i==4时,应该把上上次出现当2的位置delt,把上次出现的2的位置add,并且记录当前位置.
并且处理b[idx].r<=i的查询.因为此时的查询是不漏不重的.
采花--和上一题的思想很像很像--线段树TLE了,改成树状数组试试看--AC了
https://www.luogu.com.cn/problem/P4113
void solve(){
    cin>>n>>k>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++) {
        cin>>b[i].l>>b[i].r;
        b[i].idx0=i;
    }
    sort(b+1,b+m+1);
    int idx=1;
    for(int i=1;i<=n;i++){
        if(mark[a[i]].first==0) mark[a[i]].first=i;
        else if(mark[a[i]].second==0) {
            mark[a[i]].second=i;
            add(mark[a[i]].first);  单点修改
        }
        else if(mark[a[i]].first<mark[a[i]].second){
            delt(mark[a[i]].first);
            add(mark[a[i]].second);
            mark[a[i]].first=i;
        }
        else if(mark[a[i]].first>mark[a[i]].second){
            delt(mark[a[i]].second);
            add(mark[a[i]].first);
            mark[a[i]].second=i;
        }
        while(idx<=m&&b[idx].r<=i){     可取等
            ans[b[idx].idx0]=query(b[idx].r)-query(b[idx].l-1);
            idx++;
        }
        if(idx>m) break;
    }
    while(idx<=m){
        ans[b[idx].idx0]=query(b[idx].r)-query(b[idx].l-1);
        idx++;
    }
    for(int i=1;i<=m;i++) cout<<ans[i]<<endl;
}

总结:洛谷紫题。上一题是这题的铺垫。离线处理。详细见注解。

数据结构 (nowcoder.com)

const int N=10004;
typedef struct node{
    int p,l,r,sum1,sum2,tag1,tag2;
}node;
node tr[4*N];
int n,m;
int arr[N];
void pushup(int p){
    tr[p].sum1=tr[lc].sum1+tr[rc].sum1;
    tr[p].sum2=tr[lc].sum2+tr[rc].sum2;
}
先下放乘的tag2,再下放加的tag1.
因为tag2的变动会影响tag1,而tag1的变动不会影响tag2.
tag2变动时对tag1的影响已经处理了.
int quickpow(int a,int b){
    int res=1;
    while(b){
        if(b&1) res*=a;
        a=a*a;
        b>>=1;
    }
    return res;
}
void pushdown(int p){
    int tag1=tr[p].tag1;
    int tag2=tr[p].tag2;
    if(tag2!=1){          乘法懒标记
        tr[lc].sum1*=tag2;
        tr[rc].sum1*=tag2;
        tr[lc].sum2*=quickpow(tag2,2);
        tr[rc].sum2*=quickpow(tag2,2);
        tr[lc].tag2*=tag2,tr[lc].tag1*=tag2;mark1:需要更新tag1吗..?数据太弱了,这里把tag1乱改/删去都能过
        tr[rc].tag2*=tag2,tr[rc].tag1*=tag2;经过自测样例,这两行是需要的,但是评测居然测不出来..
        tr[p].tag2=1;
    }
    if(tag1){           加法懒标记
        sum2需要的是原本的sum1值,先更新sum2.     ps:先更新sum2
        tr[lc].sum2+=(tr[lc].r-tr[lc].l+1)*tag1*tag1+2*tag1*(tr[lc].sum1);  这两行tag1全写成了tag2..
        tr[rc].sum2+=(tr[rc].r-tr[rc].l+1)*tag1*tag1+2*tag1*(tr[rc].sum1);
        tr[lc].sum1+=(tr[lc].r-tr[lc].l+1)*tag1;
        tr[rc].sum1+=(tr[rc].r-tr[rc].l+1)*tag1;
        tr[lc].tag1+=tag1;
        tr[rc].tag1+=tag1;
        tr[p].tag1=0;
    }
}
void build(int p,int l,int r){
    tr[p]={p,l,r,0,0,0,1};           mark2:tag2初始值为1..--ok
    if(l==r){
        tr[p].sum1=arr[l];
        tr[p].sum2=arr[l]*arr[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(p);
}
void add(int p,int l,int r,int x){
    if(l<=tr[p].l&&r>=tr[p].r){
        tr[p].sum2+=(tr[p].r-tr[p].l+1)*x*x+2*x*(tr[p].sum1);   mark3:先更新sum2--ok
        tr[p].sum1+=(tr[p].r-tr[p].l+1)*x;
        tr[p].tag1+=x;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) add(lc,l,r,x);
    if(r>mid) add(rc,l,r,x);
    pushup(p);
}
void multi(int p,int l,int r,int x){
    if(l<=tr[p].l&&r>=tr[p].r){
        tr[p].sum1*=x;
        tr[p].sum2*=quickpow(x,2);
        tr[p].tag2*=x;
        mark4:这样处理tag1对sum2的影响是正确的吗..? 把这行注释了也能过...但是在这里乱改tag1又会wa
        经过自测样例,这一行是需要的,但是评测居然测不出来..
        tr[p].tag1*=x;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) multi(lc,l,r,x);
    if(r>mid) multi(rc,l,r,x);
    pushup(p);
}
int query1(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r) return tr[p].sum1;
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    int res=0;
    if(l<=mid) res+=query1(lc,l,r);
    if(r>mid) res+=query1(rc,l,r);
    return res;
}
int query2(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r) return tr[p].sum2;
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    int res=0;
    if(l<=mid) res+=query2(lc,l,r);
    if(r>mid) res+=query2(rc,l,r);
    return res;
}
数据结构--维护区间和,区间元素的平方 和,区间加,区间乘
https://ac.nowcoder.com/acm/problem/19246
void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>arr[i];
    build(1,1,n);
    int op,l,r,x;
    while(m--){
        cin>>op>>l>>r;
        if(op==1) cout<<query1(1,l,r)<<endl;
        else if(op==2) cout<<query2(1,l,r)<<endl;
        else if(op==3){
            cin>>x;
            multi(1,l,r,x);
        }
        else if(op==4){
            cin>>x;
            add(1,l,r,x);
        }
    }
}
//自敲样例--可以测mark1和mark4:
42 882--expect
10 7
2 2 2 2 2 2 2 2 2 2
4 1 3 2
4 1 10 1
3 1 5 2
3 1 3 2
4 1 2 1
1 1 2
2 1 2

总结:比后面那题少头疼一点点。先处理乘法tag再处理加法tag

线段树 (nowcoder.com)

const int N=100005;
typedef struct node{
    int p,l,r,sum1,sum2,tag1,tag2;  sum1是区间和,sum2是区间两两相乘之和,tag1为加法懒标记,tag2为乘法懒标记.
}node;
node tr[4*N];
int n,m,mod;
int arr[N];
void pushup(int p){
    tr[p].sum1=(tr[lc].sum1+tr[rc].sum1)%mod;
    简单推一下公式易知--少了个右sum2!!!!
    tr[p].sum2=((tr[lc].sum2+tr[rc].sum2)%mod+(tr[lc].sum1*tr[rc].sum1)%mod)%mod;
}//ok
void pushdown(int p){           sum1,sum2的更新..
    int tag1=tr[p].tag1%mod;
    int tag2=tr[p].tag2%mod;
//    if(tag1){
//    (tr[lc].sum2+=((tag1*(tr[lc].r-tr[lc].l))%mod*tr[lc].sum1)%mod+
//                  (tag1*tag1)%mod*(tr[lc].r-tr[lc].l+1-1)*(1+tr[lc].r-tr[lc].l)/2%mod)%=mod;
//    (tr[rc].sum2+=((tag1*(tr[rc].r-tr[rc].l))%mod*tr[rc].sum1)%mod+
//                  (tag1*tag1)%mod*(tr[rc].r-tr[rc].l+1-1)*(1+tr[rc].r-tr[rc].l)/2%mod)%=mod;
//    }
    if(tag2!=1){
        (tr[lc].sum2*=(tag2*tag2)%mod)%=mod;
        (tr[rc].sum2*=(tag2*tag2)%mod)%=mod;
        (tr[lc].sum1*=tag2)%=mod;
        (tr[rc].sum1*=tag2)%=mod;
        (tr[lc].tag2*=tag2)%=mod,(tr[lc].tag1*=tag2)%=mod;  mark
        (tr[rc].tag2*=tag2)%=mod,(tr[rc].tag1*=tag2)%=mod;
        tr[p].tag2=1;
    }
    if(tag1){
        警钟长鸣:算等差数列/2的地方不要乱取mod,算完再取mod,不然遇到奇数/2直接褒姒!!!
        (tr[lc].sum2+=((tag1*(tr[lc].r-tr[lc].l))%mod*tr[lc].sum1)%mod+
                (tag1*tag1)%mod*(tr[lc].r-tr[lc].l+1-1)*(1+tr[lc].r-tr[lc].l)/2%mod)%=mod;
        (tr[rc].sum2+=((tag1*(tr[rc].r-tr[rc].l))%mod*tr[rc].sum1)%mod+
                (tag1*tag1)%mod*(tr[rc].r-tr[rc].l+1-1)*(1+tr[rc].r-tr[rc].l)/2%mod)%=mod;
        (tr[lc].sum1+=((tr[lc].r-tr[lc].l+1)*tag1)%mod)%=mod;
        (tr[rc].sum1+=((tr[rc].r-tr[rc].l+1)*tag1)%mod)%=mod;
        (tr[lc].tag1+=tag1)%=mod;
        (tr[rc].tag1+=tag1)%=mod;
        tr[p].tag1=0;
    }
}
加乘加 乘加乘
void build(int p,int l,int r){
    tr[p]={p,l,r,0,0,0,1};
    if(l==r){
        tr[p].sum1=arr[l];
        tr[p].sum2=0;
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(p);
}
void add(int p,int l,int r,int x){  逐一检查sum2 sum1的公式
    if(l<=tr[p].l&&r>=tr[p].r){
        警钟长鸣:算等差数列/2的地方不要乱取mod,算完再取mod,不然遇到奇数/2直接褒姒!!!
        (tr[p].sum2+=((x*(tr[p].r-tr[p].l))%mod*tr[p].sum1)%mod  x*(区间长度-1)
                +(x*x)%mod*(tr[p].r-tr[p].l+1-1)*(1+tr[p].r-tr[p].l)/2%mod)%=mod;后面这一坨是等差数列求和
        (tr[p].sum1+=(x*(tr[p].r-tr[p].l+1))%mod)%=mod;
        (tr[p].tag1+=x)%=mod;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) add(lc,l,r,x);
    if(r>mid) add(rc,l,r,x);
    pushup(p);
}
void multi(int p,int l,int r,int x){
    if(l<=tr[p].l&&r>=tr[p].r){
        (tr[p].sum2*=(x*x)%mod)%=mod;
        (tr[p].sum1*=x)%=mod;
        (tr[p].tag2*=x)%=mod;
        (tr[p].tag1*=x)%=mod;  mark
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) multi(lc,l,r,x);
    if(r>mid) multi(rc,l,r,x);
    pushup(p);
}
pair<int,int> query(int p,int l,int r){           用merge
    if(l<=tr[p].l&&r>=tr[p].r) return {tr[p].sum1,tr[p].sum2};
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l>mid) return query(rc,l,r);
    if(r<=mid) return query(lc,l,r);
    pair<int,int> askl=query(lc,l,r),askr=query(rc,l,r);
    return {(askl.first+askr.first)%mod,((askl.second+askr.second)%mod+(askl.first*askr.first)%mod)%mod};  {sum1,sum2}
}
深刻理解tag1和tag2.!
深刻理解pushdown!!,即使ac了,还是不是很清楚为什么当tag2,tag1同时存在时,tag1更新sum2时,用的sum1是tag2更新后的sum1..
线段树
https://ac.nowcoder.com/acm/problem/212880
void solve(){                   这个题要疯狂取mod,数字非常庞大.---多取不是乱取,在等差数列那里乱取直接褒姒!!
    cin>>n>>m>>mod;
    for(int i=1;i<=n;i++) cin>>arr[i];
    build(1,1,n);
    int op,l,r,x;
    while(m--){
        cin>>op>>l>>r;
        if(op==1) {
            cin>>x;
            add(1,l,r,x);
        }
        else if(op==2){
            cin>>x;
            multi(1,l,r,x);
        }
        else if(op==3) cout<<query(1,l,r).second<<endl;
    }
}

总结:比较头疼的维护。记得先处理乘法tag再处理加法tag,然后看清楚推导的式子。

H-仓鼠的鸡蛋_牛客竞赛数据结构专题班树状数组、线段树练习题(重现赛)@IR101 (nowcoder.com)

const int N=300005;
int n,m,k;
int arr[N];
typedef struct node{
    int p,l,r,bsk,cnt;
}node;
node tr[4*N];
void pushup(int p){
    if(tr[lc].cnt<k&&tr[rc].cnt<k) tr[p].bsk=max(tr[lc].bsk,tr[rc].bsk);
    else if(tr[lc].cnt<k) tr[p].bsk=tr[lc].bsk;
    else if(tr[rc].cnt<k) tr[p].bsk=tr[rc].bsk;
    else tr[p].bsk=0;
}
void build(int p,int l,int r){
    tr[p]={p,l,r,0,0};
    if(l==r){
        tr[p].bsk=m;
        tr[p].cnt=0;
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(p);
}
void update(int p,int l,int r,int x){
    if(l<=tr[p].l&&r>=tr[p].r){
        tr[p].bsk-=x,tr[p].cnt++;
        return;
    }
    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 ask(int p,int x){        线段树二分
    if(tr[p].l==tr[p].r) return tr[p].l;
    if(tr[lc].bsk>=x&&tr[lc].cnt<k) return ask(lc,x);tr[].cnt<k,在根节点之前全为0,没问题的.但是根节点就会被判到了
    else if(tr[rc].bsk>=x&&tr[rc].cnt<k) return ask(rc,x);
    return -1;
}
仓鼠的鸡蛋
https://ac.nowcoder.com/acm/contest/86173/H
void solve(){
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) cin>>arr[i];
    build(1,1,n);
    for(int i=1;i<=n;i++){
        int res=ask(1,arr[i]);
        if(res!=-1) update(1,res,res,arr[i]);
        cout<<res<<endl;
    }
}

总结:线段树二分(ask),区间维护区间中可用的最大的篮子空间,实际上的更新都是单点更新。并且每次查询实际上都会查询到叶子节点。

[ABC322F] Vacation Query - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

const int N=500005;
typedef struct node{
    int l,r;
    int ml0,ml1,mr0,mr1,mx0,mx1;
    int tag;
}node;
node tr[N<<2];
int n,q;
string str;
node merge(node trlc,node trrc){
    node res;
    res.mx0=max({trlc.mx0,trrc.mx0,trlc.mr0+trrc.ml0});直接拼,如果拼不了(那个值也不会取到),也不会影响答案
    res.mx1=max({trlc.mx1,trrc.mx1,trlc.mr1+trrc.ml1});
    左端
    trlc.ml0==trlc.r-trlc.l+1?res.ml0=trlc.ml0+trrc.ml0:res.ml0=trlc.ml0;
    trlc.ml1==trlc.r-trlc.l+1?res.ml1=trlc.ml1+trrc.ml1:res.ml1=trlc.ml1;
    右端
    trrc.mr0==trrc.r-trrc.l+1?res.mr0=trlc.mr0+trrc.mr0:res.mr0=trrc.mr0;
    trrc.mr1==trrc.r-trrc.l+1?res.mr1=trlc.mr1+trrc.mr1:res.mr1=trrc.mr1;
    其余的值全部都要更新
    res.l=trlc.l,res.r=trrc.r,res.tag=0;
    return res;
}
void pushdown(int p){
    int tag=tr[p].tag;
    if(tag&1){
        swap(tr[lc].ml0,tr[lc].ml1);
        swap(tr[lc].mr0,tr[lc].mr1);
        swap(tr[lc].mx0,tr[lc].mx1);

        swap(tr[rc].ml0,tr[rc].ml1);
        swap(tr[rc].mr0,tr[rc].mr1);
        swap(tr[rc].mx0,tr[rc].mx1);

        tr[lc].tag+=tag;
        tr[rc].tag+=tag;
        tr[p].tag=0;
    }
}
void build(int p,int l,int r){
    tr[p]={l,r,0,0,0,0,0,0,0};
    if(l==r){
        if(str[l-1]=='0') tr[p].ml0=1,tr[p].mr0=1,tr[p].mx0=1;
        if(str[l-1]=='1') tr[p].ml1=1,tr[p].mr1=1,tr[p].mx1=1;
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    tr[p]=merge(tr[lc],tr[rc]);
}
void update(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r){
        swap(tr[p].ml0,tr[p].ml1);
        swap(tr[p].mr0,tr[p].mr1);
        swap(tr[p].mx0,tr[p].mx1);
        tr[p].tag++;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) update(lc,l,r);
    if(r>mid) update(rc,l,r);
    tr[p]=merge(tr[lc],tr[rc]);
}

node query(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r) return tr[p];       l>=..
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l>mid) return query(rc,l,r);
    if(r<=mid) return query(lc,l,r);
    return merge(query(lc,l,r),query(rc,l,r));
}
Vacation Query
//https://www.luogu.com.cn/problem/AT_abc322_f
void solve(){                 G
    cin>>n>>q;
    cin>>str;
    build(1,1,n);
    while(q--){
        int op,l,r;
        cin>>op>>l>>r;
        if(op==1) update(1,l,r);
        if(op==2) cout<<query(1,l,r).mx1<<endl;
    }
}

总结:该题要维护01串的区间最大连续1的个数,并且要区间修改,翻转01。实现更新直接swap就好了,还有注意merge直接取max即可,不用判中间是否字符是否相同,如果不同的话也不会影响取max的结果。该题开辟了merge的应用,以后就用merge吧,比pushup的作用大很多,还可以用在query,非常关键,非常有用,非常好用.

2024“钉耙编程”中国大学生算法设计超级联赛(3) - Virtual Judge (vjudge.net)

Problem - 7463 (hdu.edu.cn)

思路:周日爽切,wa了一发小错,没有判到询问区间相等的区间长度为1的情况. 思维上不难想,主要是写merge比较麻烦,跟PTA模拟题一样,讨论清楚各种情况,注意细节!其他的没什么了。主要是merge的分类讨论!

const int N=300005;
typedef struct node{
    int l,r,lnum,rnum,tag; tag记录加法
    int sta;  当前区间的状态0,1,2,3,4,5;分别代表:单独一个点,相同,严格升,严格降,单峰,什么都不是.
}node;
node tr[4*N];
int n,q;
int arr[N];
详细讨论清楚
node merge(node tlc,node trc){
    node res={0,0,0,0,0,0};
    分类讨论各种情况,确定res的sta.
    if(tlc.sta==5||trc.sta==5) res.sta=5;
    else if(tlc.sta==0&&trc.sta==0){  两个都是单独的
        if(tlc.lnum>trc.rnum) res.sta=3;
        if(tlc.lnum==trc.rnum) res.sta=1;
        if(tlc.lnum<trc.rnum) res.sta=2;
    }
    else if(tlc.sta==0){        左是单独的
        if(trc.sta==1) tlc.rnum==trc.rnum?res.sta=1:res.sta=5;
        if(trc.sta==2) tlc.rnum<trc.lnum?res.sta=2:res.sta=5;
        if(trc.sta==3) tlc.rnum==trc.lnum?res.sta=5:tlc.rnum>trc.lnum?res.sta=3:res.sta=4; 注意
        if(trc.sta==4) tlc.rnum<trc.lnum?res.sta=4:res.sta=5;
    }
    else if(trc.sta==0){      右是单独的
        if(tlc.sta==1) trc.lnum==tlc.rnum?res.sta=1:res.sta=5;
        if(tlc.sta==2) trc.lnum==tlc.rnum?res.sta=5:trc.lnum>tlc.rnum?res.sta=2:res.sta=4; 注意
        if(tlc.sta==3) trc.lnum<tlc.rnum?res.sta=3:res.sta=5;
        if(tlc.sta==4) trc.lnum<tlc.rnum?res.sta=4:res.sta=5;
    }
    else{   左右都不是0,5
        1情况
        if(tlc.sta==1&&trc.sta==1&&tlc.rnum==trc.lnum) res.sta=1;
        else if(tlc.sta==1||trc.sta==1) res.sta=5;
        2,3,4情况
        else if(tlc.sta==2){
            if(trc.sta==2) tlc.rnum<trc.lnum?res.sta=2:res.sta=5;
            else if(trc.sta==3) tlc.rnum!=trc.lnum?res.sta=4:res.sta=5;
            else if(trc.sta==4) tlc.rnum<trc.lnum?res.sta=4:res.sta=5;
        }
        else if(tlc.sta==3){
            if(trc.sta==2) res.sta=5;
            if(trc.sta==3) tlc.rnum>trc.lnum?res.sta=3:res.sta=5;
            if(trc.sta==4) res.sta=5;
        }
        else if(tlc.sta==4){
            if(trc.sta==2) res.sta=5;
            if(trc.sta==3) tlc.rnum>trc.lnum?res.sta=4:res.sta=5;
            if(trc.sta==4) res.sta=5;
        }
    }
    res.l=tlc.l,res.r=trc.r;
    res.lnum=tlc.lnum,res.rnum=trc.rnum;
    res.tag=0;
    return res;
}
void pushup(int p){ tr[p]=merge(tr[lc],tr[rc]); }
void pushdown(int p){
    int tag=tr[p].tag;
    if(tag){
        tr[lc].lnum+=tag,tr[lc].rnum+=tag,tr[lc].tag+=tag;
        tr[rc].lnum+=tag,tr[rc].rnum+=tag,tr[rc].tag+=tag;
        tr[p].tag=0;
    }
}
void build(int p,int l,int r){
    tr[p]={l,r,0,0,0,0};
    if(l==r){
        tr[p].lnum=arr[l];
        tr[p].rnum=arr[r];
        tr[p].sta=0;
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(p);
}
void add(int p,int l,int r,int x){
    if(l<=tr[p].l&&r>=tr[p].r){
        tr[p].lnum+=x,tr[p].rnum+=x,tr[p].tag+=x;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) add(lc,l,r,x);
    if(r>mid) add(rc,l,r,x);
    pushup(p);
}
node query(int p,int l,int r){
    if(l<=tr[p].l&&r>=tr[p].r) return tr[p];
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l>mid) return query(rc,l,r);
    if(r<=mid) return query(lc,l,r);
    return merge(query(lc,l,r),query(rc,l,r));
}
#define print0 cout<<"0"<<endl
#define print1 cout<<"1"<<endl
G - 单峰数列
https://vjudge.net/contest/643851#problem/G
https://acm.hdu.edu.cn/showproblem.php?pid=7463
一发入魂!! 小错不算错,wa的一发是询问区间是否相等,然后个数是1,没判到. 加了ask.sta==0即可.  还是一发入魂!
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>arr[i];
    build(1,1,n);
    cin>>q;
    int l,r,x;
    while(q--){
        int op; cin>>op>>l>>r;
        if(op==1){
            cin>>x,add(1,l,r,x);
            continue;
        }
        node ask=query(1,l,r);
        if(op==2) ask.sta==0||ask.sta==1 ? print1 : print0;
        if(op==3) ask.sta==0||ask.sta==2 ? print1 : print0;
        if(op==4) ask.sta==0||ask.sta==3 ? print1 : print0;
        if(op==5) ask.sta==4 ? print1 : print0;
    }
}

Problem - 1796D - Codeforces--Maximum Subarray

思路:显而易见的是,当x为正的时候,选择长度为k的连续区间总是最优的,因为这样最可能被最大的连续区间包含. 当x为负的时候,只需要简单转化一下:k=n-k,x=-x; (线段树写法不担心k的规模)

o(n)枚举长度为k的连续区间。o(logn)查询每次枚举之后的最大子段和。

线段树写法:
显而易见的是,当x为正的时候,选择长度为k的连续区间总是最优的,因为这样最可能被最大的连续区间包含.
当x为负的时候,只需要简单转化一下:k=n-k,x=-x; (线段树写法不担心k的规模)
枚举连续的长度为k的区间为所选.用线段树logn即可求得当前序列的最大子段和.
#define lc p<<1
#define rc p<<1|1
int n,k,x;
int arr[200005];
typedef struct node{
    int l,r,lmx,rmx,mx,sum;
}node;
node tr[4*200005];
inline node merge(node llc,node rrc){
    node res={llc.l,rrc.r,0,0,0,0};
    res.sum=llc.sum+rrc.sum;
    res.mx=max({llc.mx,rrc.mx,llc.rmx+rrc.lmx});
    res.lmx=max(llc.lmx,llc.sum+rrc.lmx);   这里不用分类讨论,rrc.lmx可能非常大,可以弥补llc.sum的负影响
    res.rmx=max(rrc.rmx,rrc.sum+llc.rmx);
    return res;
}
inline void build(int p,int l,int r){
    tr[p]={l,r,0,0,0,0};
    if(l==r){
        tr[p].lmx=arr[l];
        tr[p].rmx=arr[l];
        tr[p].mx=arr[l];
        tr[p].sum=arr[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    tr[p]=merge(tr[lc],tr[rc]);
}
inline void update(int p,int l,int r,int xx){
    if(l<=tr[p].l&&r>=tr[p].r){
        tr[p].lmx+=xx;
        tr[p].rmx+=xx;
        tr[p].mx+=xx;
        tr[p].sum+=xx;
        return;
    }
    单点修改不需要pushdown
    int mid=(tr[p].l+tr[p].r)>>1;
    if(l<=mid) update(lc,l,r,xx);
    if(r>mid) update(rc,l,r,xx);
    tr[p]=merge(tr[lc],tr[rc]);
}
https://codeforces.com/problemset/problem/1796/D
void solve(){      D     线段树写法
    cin>>n>>k>>x;
    int ans=0;
    if(x<0) k=n-k,x=-x;
    for(int i=1;i<=n;i++) {
        cin>>arr[i],arr[i]-=x;
        if(i<=k) arr[i]+=2*x;
    }
    build(1,1,n);
    for(int i=k;i<=n;i++) {
        ans=max(ans,tr[1].mx);
        if(i<=n-1){
            update(1,i-k+1,i-k+1,-2*x);
            update(1,i+1,i+1,2*x);
        }
    }
    cout<<ans<<endl;
}

to be continue...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值