Burdock blogDay1-Differential Array-Segment Tree

Simple examples

Give the interval where we add x
A very simple title
It's the easiest idea to give code, but it certainly won't work.

#include <iostream>
using namespace std;
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _for(i,a,b) for(int i=(a);i<(b);++i)
const int MAXN = 1e5 +6;
int t;
int m,n;
int a[MAXN];
int readint(){int x;scanf("%d",&x);return x;}
int main(){
    t=readint();
    while(t--){
        n=readint();
        _rep(i,1,n) a[i]=readint();
        m=readint();
        while(m--){
            int l=readint();
            int r=readint();
            int x=readint();
            _rep(i,l,r) a[i]+=x;
        }
        _rep(i,1,n) if(i!=n) cout<<a[i]<<" ";else cout<<a[i];
        cout<<endl;
    }
    return 0;
}

Code estimate is tle
High algorithm complexity is definitely not a good problem
Next, use the idea of difference

Differential

The code above shows that the complexity should be O(N2)
For slightly larger data, tle
That must not work.
So we need information to add x to the record interval of another array
How to optimize is essentially to reduce the use of loops
So in

        while(m--){
            int l=readint();
            int r=readint();
            int x=readint();
            _rep(i,l,r) a[i]+=x;
        }

In this passage we find that while has another for
This complexity increases in a flash
We define an aux array to record m interval operations
So that's the core code

        while(m--){
            int l=readint();
            int r=readint();
            int x=readint();
            aux[l]+=x;
            aux[r+1]-=x;
        }

aux array l+=x, r+1-=x
Why?
You will need the following operation explanation

_rep(i,2,n) aux[i]+=aux[i-1];

Array plus the number before itself
For example, aux3+=3
aux6-=3
At first the aux arrays were all 0
So aux4=aux4+aux3
So aux4=3
Until n ends
So what if it exceeds the interval r to be modified?
So at aux6-=3
aux6=-3 After other treatments aux6=aux6+aux5 becomes 0
Traverse to n followed by 0
This ensures that the information in the L-R interval is stored in aux
From the above we can see simply how many times aux has been manipulated
That is to say that aux has been manipulated m times and still holds all the information in the interval

_rep(i,1,n) {a[i]+=aux[i];if(i!=n)cout<<a[i]<<" ";else cout<<a[i]<<endl;}

a[i]+=aux[i] before output;
Modify a with information from aux
We can see that
Loop becomes a problem where there are no loops and there are loops in the loop
Complexity comes to O(N)

So that's it
Complete code

#include <iostream>
using namespace std;
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _for(i,a,b) for(int i=(a);i<(b);++i)
inline int readint(){int x;scanf("%d",&x);return x;}
const int MAXN=1e5+5;
int t;
int m,n;
int a[MAXN];
int aux[MAXN];
int main(){
    t=readint();
    while(t--){
        n=readint();
        _rep(i,1,n) a[i]=readint();
        m=readint();
        while(m--){
            int l=readint();
            int r=readint();
            int x=readint();
            aux[l]+=x;
            aux[r+1]-=x;
        }
        _rep(i,2,n) aux[i]+=aux[i-1];
        _rep(i,1,n) {a[i]+=aux[i];if(i!=n)cout<<a[i]<<" ";else cout<<a[i]<<endl;}
    }
    return 0;
}

Is there anything faster than O(N)?
There seems to be an O(logN) on the complexity table

Segment tree

This is not clear to me either.
Simply put, this information is maintained using a binary tree and a complete binary tree
First

Build Trees
void build(int l,int r,int rt){
    add[rt]=0;
    if(l==r){
        scanf("%lld",&sum[rt]);
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);//Build Tree to Bottom Exit Recursively to Penultimate Layer 2 Down with New Layer Exit Layer and Update Layer Down Complete Update Intervals and
    push_up(rt);
    
}

Here we simplify the code with a macro definition

#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

Moving left is equivalent to multiplying by two
The one below is equivalent to multiplying by two times+1
Say nothing more
The code clearly shows the data we entered at the bottom of the tree
Their parent nodes are used to record interval sums

Two update operations
void push_up(int rt){//Update
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void push_down(int rt,int m){
    if(add[rt]){
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum[rt<<1]+=(m-(m>>1))*add[rt];
        sum[rt<<1|1]+=(m>>1)*add[rt];
        //Update Down
        add[rt]=0;
    }
}

add is a lazy tag
What I understand about the information used to record changes
Because when we revisit the sum of intervals, we find that after modifying a value
Need to keep updating down so this tm isn't obsolete
So when you need to use it, visit it and update it
The push down operation is obviously a downward update operation
push up is an operation that updates

Two important operations

Obviously I don't know what to call

void update(int a,int b,long long c,int l,int r,int rt){
    if(a<=l && b>=r){
        sum[rt]+=(r-l+1)*c;//Update Interval Sum
        add[rt]+=c;//Add lazy tag
        return;
    }
    push_down(rt,r-l+1);//Update Modified Intervals Down
    int mid=(l+r)>>1;
    if(a<=mid) update(a,b,c,lson);
    if(b>mid) update(a,b,c,rson);
    push_up(rt);
}
long long query(int a,int b,int l,int r,int rt){
    if(a<=l && b>=r) return sum[rt];//The searched interval already contains 
    //Not included to access below so update down
    push_down(rt,r-l+1);
    int mid=(l+r)>>1;
    long long ans=0;
    if(a<=mid) ans+=query(a,b,lson);
    if(b>mid) ans+=query(a,b,rson);
    return ans;
}

The first function is to modify the interval operation
Parameter a b c is the interval to be modified and the modified value This modification is to add this value
The interval found in update that the interval being checked contains the node in which it is located updates the interval, adds lazy tag, and return s his task is over
So some questions have returned intervals that are not all but other values of the node intervals?
Of course, this is a recursive function where the other numbers are provided by another stack frame return
If not included
Guarantee zero in add for downward updates
If a<=mid we check the left side once
If b>mid we check right once
Because we defined mid as left subtree
So a has an equal sign which I understand
Finally, the task of our stack frame is finished.

The second function is similar
We find that if we include the interval of this node, we return the interval sum of the nodes
Otherwise we look left and right and return ans
The idea of completing a task is the same as that of the previous function

The operation of the segment tree is gone here
Next comes the fairy main we're going to solve the difference problem
But we'll have a surprising discovery

int main(){
    t=readint();
    while(t--){
        n=readint();
        build(1,n,1);
        m=readint();
        while(m--){
            int l=readint();
            int r=readint();
            int x=readint();
            update(l,r,x,1,n,1);
        }
        _rep(i,1,n) if(i!=n) cout<<query(i,i,1,n,1)<<" ";else cout<<query(i,i,1,n,1)<<endl;
    }
    return 0;
}

We were stunned when we returned this value

query(i,i,1,n,1)

What exactly is this operation?
Does this access each return not necessarily go to the bottom child node?
So complexity is not O(N)
Avoid startling for the benefits of segment trees
So we know that the last data is at the bottom because this is a complete binary tree
So we can add a variable
1<<(int(log(n)/log(2))+1)
This calculates the subscript of the lowest node in the array
Such an operation, however, seems to only beautify the logic of the code
So nothing has been done to optimize emmm

Here's all the code

#include <iostream>
using namespace std;
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _for(i,a,b) for(int i=(a);i<(b);++i)
inline int readint(){int x;scanf("%d",&x);return x;}
const int MAXN=1e5 +10;
long long sum[4*MAXN];
long long add[4*MAXN];//add is a lazy tag
int t,n,m;
void push_up(int rt){//Update
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void push_down(int rt,int m){
    if(add[rt]){
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum[rt<<1]+=(m-(m>>1))*add[rt];
        sum[rt<<1|1]+=(m>>1)*add[rt];
        //Update Down
        add[rt]=0;
    }
}
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

void build(int l,int r,int rt){
    add[rt]=0;
    if(l==r){
        scanf("%lld",&sum[rt]);
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);//Build Tree to Bottom Exit Recursively to Penultimate Layer 2 Down with New Layer Exit Layer and Update Layer Down Complete Update Intervals and
    push_up(rt);
    
}

void update(int a,int b,long long c,int l,int r,int rt){
    if(a<=l && b>=r){
        sum[rt]+=(r-l+1)*c;//Update Interval Sum
        add[rt]+=c;//Add lazy tag
        return;
    }
    push_down(rt,r-l+1);//Update Modified Intervals Down
    int mid=(l+r)>>1;
    if(a<=mid) update(a,b,c,lson);
    if(b>mid) update(a,b,c,rson);
    push_up(rt);
}
long long query(int a,int b,int l,int r,int rt){
    if(a<=l && b>=r) return sum[rt];//The searched interval already contains 
    //Not included to access below so update down
    push_down(rt,r-l+1);
    int mid=(l+r)>>1;
    long long ans=0;
    if(a<=mid) ans+=query(a,b,lson);
    if(b>mid) ans+=query(a,b,rson);
    return ans;
}
int main(){
    t=readint();
    while(t--){
        n=readint();
        build(1,n,1);
        m=readint();
        while(m--){
            int l=readint();
            int r=readint();
            int x=readint();
            update(l,r,x,1,n,1);
        }
        _rep(i,1,n) if(i!=n) cout<<query(i,i,1,n,1)<<" ";else cout<<query(i,i,1,n,1)<<endl;
    }
    return 0;
}

Segment trees are not just solutions + problems that can be modified first.
Many of my understandings may be incorrect
😴Good night

Tags: C++ Algorithm

Posted on Mon, 27 Sep 2021 12:44:33 -0400 by Boudga