线段树模板

线段树

    • 线段树
      • 线段树最裸模板
      • 线段树区间修改Lazy大法
      • 离散化 线段树
      • 线段树求某值之前或之后与其差值不小于k的最近位置
      • 相关内容
      • 由2延伸修改
      • 多校一道有意思的题
      • 线段树训练题单

  • 不要忘记初始建树 build(….)
  • 不要把区间节点与树节点混淆

线段树的区间查询有两种方式,不同写法的功能不同,要多加注意

两者区别很小,前者(l, r)初始区间得到保留,而后者将(l, r)拆散求和
但是其原理都是将大区间拆散求和,主要是因为形式不同对于不同性质问题求解麻烦与否有很大影响
I.
int query(int l, int r, int L, int R, int root)  
{  
    int res = 0;  
    if(l <= L && r >= R) return sum[root];  
    int mid = (L + R) >> 1;  
    if(l <= mid) res += query(l, r, L, mid, root<<1);  
    if(r > mid)  res += query(l, r, mid+1, R, root<<1|1);  
    return res;  
}  

II.
int Query(int l, int r, int L, int R, int root)
{
    if(l<=L && r>=R)
        return sum[root];

    int mid = (L + R) >> 1;
    if(r <= mid) return Query(l, r, Lson);
    else if(l > mid) return Query(l, r, Rson);
    else return Query(l, mid, Lson) + Query(mid+1, r, Rson);
}

1.线段树最裸模板

Problem I

#include
#include
#include
using namespace std;
const int MAX=50010;
#define Lson L,mid,root<<1 //遇到Lson的时候强制替换为后面的语句
#define Rson mid+1,R,root<<1|1
int n,sum[MAX<<2];

void Pushup(int root) //把当前结点的信息更新到父节点
{
    sum[root]=sum[root<<1]+sum[root<<1|1];
}

void Build(int L,int R,int root)
{
    if(L==R)
    {
        scanf("%d",&sum[root]);
        return ;
    }
    int mid=(L+R)>>1;
    Build(Lson); //左孩子
    Build(Rson); //右孩子
    Pushup(root);
}

void Update(int q,int val,int L,int R,int root) //在根为root,区间为[L,R]中的线段树修改结点p的值增加val
{
    if(L==R)
    {
        sum[root]+=val;
        return ;
    }
    int mid=(L+R)>>1;
    if(q<=mid) Update(q,val,Lson); //说明p在左结点
    else Update(q,val,Rson); //说明p在右结点
    Pushup(root);
}

int Query(int l, int r, int L, int R, int root)
{
    if(l<=L && r>=R)
        return sum[root];

    int mid = (L + R) >> 1;
    if(r <= mid) return Query(l, r, Lson);
    else if(l > mid) return Query(l, r, Rson);
    else return Query(l, mid, Lson) + Query(mid+1, r, Rson);
}

int main()
{
    int a,b,Case,num=1;
    scanf("%d",&Case);
    while(Case--)
    {
        printf("Case %d:\n",num++);
        scanf("%d",&n);
        Build(1,n,1);
        char op[10];
        while(scanf("%s",op))
        {
            if(op[0]=='E') break;
            scanf("%d%d",&a,&b);
            if(op[0]=='A') Update(a,b,1,n,1);
            if(op[0]=='S') Update(a,-b,1,n,1);
            if(op[0]=='Q') printf("%d\n",Query(a,b,1,n,1));
        }
    }
    return 0;
}

2.线段树区间修改—Lazy大法

Problem II

#include 
#include 

#define maxn 100000 + 10
#define Lson L, mid, rt<<1
#define Rson mid+1, R, rt<<1|1

struct Node
{
    int sum, lazy;
} T[maxn<<2];

void PushUp(int rt)
{
    T[rt].sum = T[rt<<1].sum + T[rt<<1|1].sum;
}

void PushDown(int L, int R, int rt)
{
    int mid = (L + R) >> 1;
    T[rt<<1].sum = T[rt].lazy * (mid - L + 1);
    T[rt<<1|1].sum = T[rt].lazy * (R - mid);
    T[rt<<1].lazy = T[rt].lazy;
    T[rt<<1|1].lazy = T[rt].lazy;
    T[rt].lazy = 0;
}

void Build(int L, int R, int rt)
{
    if(L == R)
    {
        scanf("%d", &T[rt].sum);
        return ;
    }
    int mid = (L + R) >> 1;
    Build(Lson);
    Build(Rson);
    PushUp(rt);
}

void Update(int l, int r, int v, int L, int R, int rt)
{
    if(l<=L && r>=R)
    {
        T[rt].lazy = v;
        T[rt].sum = v * (R - L + 1);
        return ;
    }

    int mid = (L + R) >> 1;
    if(T[rt].lazy) PushDown(L, R, rt);

    if(r <= mid) Update(l, r, v, Lson);
    else if(l > mid) Update(l, r, v, Rson);
    else
    {
        Update(l, mid, v, Lson);
        Update(mid+1, r, v, Rson);
    }
    PushUp(rt);
}

int Query(int l, int r, int L, int R, int rt)
{
    if(l<=L && r>=R)
        return T[rt].sum;

    int mid = (L + R) >> 1;
    if(T[rt].lazy) PushDown(L, R, rt);

    if(r <= mid) return Query(l, r, Lson);
    else if(l > mid) return Query(l, r, Rson);
    return Query(l, mid, Lson) + Query(mid + 1, r, Rson);
}

int main()
{
    int n, q;
    scanf("%d", &n);
    Build(1, n, 1);
    scanf("%d", &q);
    int a, b, c, d;
    while(q--)
    {
        scanf("%d%d%d", &a, &b, &c);
        if(a)
        {
            scanf("%d", &d);
            Update(b, c, d, 1, n, 1);
        }
        else printf("%d\n", Query(b, c, 1, n, 1));
    }

    return 0;
}

3.离散化 + 线段树

Problem III

离散型与连续型的区别:
1.叶子节点:在离散型中,叶子节点是[i, i],而连续性中是[i, i + 1];
2.分解区间:在离散型中,一段区间是分解成为[l, m], [m + 1, r],而在连续型中,是分解成为[l, m], [m, r];
3.其他所有类似的判定问题。

#include 
#include 
#include 
#include 
#include 

using namespace std;
#define maxn 100005
#define lson L, mid, rt<<1
#define rson mid, R, rt<<1|1

int a[maxn], b[maxn];
int lazy[maxn<<2];
int N, L;
int left_bound = 1, right_bound;

void init()
{
    memset(lazy, -1, sizeof(lazy));
    right_bound = 0;
}

void read_compress()
{
    set<int> s;
    for(int i=1; i<=N; i++)
    {
        scanf("%d%d", &a[i], &b[i]);
        s.insert(a[i]);
        s.insert(b[i]);
    }
    map<int, int> m;
    for(set<int>::iterator it=s.begin(); it!=s.end(); it++)
        m[*it] = ++right_bound;
    for(int i=1; i<=N; i++)
    {
        a[i] = m[a[i]];
        b[i] = m[b[i]];
    }
}

void pushdown(int L, int R, int rt)
{
    if(lazy[rt] >= 0)
        lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
    lazy[rt] = -1;
}

void update(int l, int r, int v, int L, int R, int rt)
{
    if(l<=L && r>=R)
    {
        lazy[rt] = v;
        return ;
    }
    int mid = (L+R)>>1;
    if(lazy[rt] >= 0) pushdown(L, R, rt);
    if(r <= mid) update(l, r, v, lson);
    else if(l >= mid) update(l, r, v, rson);
    else
    {
        update(l, mid, v, lson);
        update(mid, r, v, rson);
    }
}

void query(int l, int r, int L, int R, int rt, set<int> &s)
{
    if(l<=L && r>=R && lazy[rt]>=0)
    {
        s.insert(lazy[rt]);
        return ;
    }
    int mid = (L+R)>>1;
    if(lazy[rt] >= 0) pushdown(L, R, rt);
    if(r <= mid) query(l, r, lson, s);
    else if(l >= mid) query(l, r, rson, s);
    else
    {
        query(l, mid, lson, s);
        query(mid, r, rson, s);
    }
}

int main()
{
    while(~scanf("%d%d", &N, &L))
    {
        init();
        read_compress();
        int cnt = 0;
        for(int i=1; i<=N; i++)
            update(a[i], b[i], ++cnt, left_bound, right_bound, 1);

        cnt = 0;
        set<int> s;
        for(int i=1; i<=N; i++)
            query(a[i], b[i], left_bound, right_bound, 1, s);
        printf("%d\n", s.size());
    }
    return 0;
}

4. 线段树求某值之前或之后与其差值不小于k的最近位置

Problem IIII

#include 
#include 
#include 
#include 

using namespace std;
#define maxn 100000 + 10
#define lson L, mid, rt<<1
#define rson mid+1, R, rt<<1|1

int n, k;
int a[maxn];
int ans;

struct Node
{
    int mi, ma;
}T[maxn<<2];

void pushup(int rt)
{
    int l = rt<<1, r = rt<<1|1;
    T[rt].ma = max(T[l].ma, T[r].ma);
    T[rt].mi = min(T[l].mi, T[r].mi);
}

void build(int L, int R, int rt)
{
    if(L == R)
    {
        T[rt].ma = T[rt].mi = a[L];
        return ;
    }
    int mid = (L + R) >> 1;
    build(lson);
    build(rson);
    pushup(rt);
}


///注意树的节点与区间节点不要混淆
void query(int l, int r, int v, int L, int R, int rt)
{
    if(L == R)
    {
        if(abs(T[rt].mi - v) >= k)
        {
            if(ans == -1 || ans < L)
                ans = L;
        }
        return ;
    }
    int mid = (L + R) >> 1;
    if(r > mid)
        if(abs(T[rt<<1|1].mi - v) >= k || abs(T[rt<<1|1].ma - v) >= k)
        query(l, r, v, rson);
    if(ans == -1 && l <= mid)
        if(abs(T[rt<<1].mi - v) >= k || abs(T[rt<<1].ma - v) >= k)
        query(l, r, v, lson);
}

int d[maxn];
long long sum;

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        sum = 1;
        scanf("%d%d", &n, &k);
        for(int i=1; i<=n; i++)
            scanf("%d", &a[i]);
        build(1, n, 1);

        d[1] = 1;
        for(int i=2; i<=n; i++)
        {
            ans = -1;
            query(1, i, a[i], 1, n, 1);
            if(ans == -1)
               d[i] = d[i-1];
            else d[i] = max(d[i-1], ans + 1);
            sum += i - d[i] + 1;
        }
        printf("%I64d\n", sum);
    }
    return 0;
}

5.相关内容

The New
Go For it

6.由2延伸修改

线段树区间修改 维护和值、最大值、最小值

#include 
#include 

#define maxn 100000 + 10
#define Lson L, mid, rt<<1
#define Rson mid+1, R, rt<<1|1

int min(int a, int b) {return aint max(int a, int b) {return a>b ? a : b;}

struct Node
{
    int sum, Min, Max, lazy;
} T[maxn<<2];

void PushUp(int rt)
{
    T[rt].sum = T[rt<<1].sum + T[rt<<1|1].sum;
    T[rt].Min = min(T[rt<<1].Min, T[rt<<1|1].Min);
    T[rt].Max = max(T[rt<<1].Max, T[rt<<1|1].Max);
}

void PushDown(int L, int R, int rt)
{
    int mid = (L + R) >> 1;
    int t = T[rt].lazy;
    T[rt<<1].sum = t * (mid - L + 1);
    T[rt<<1|1].sum = t * (R - mid);
    T[rt<<1].Min = T[rt<<1|1].Min = t;
    T[rt<<1].Max = T[rt<<1|1].Max = t;
    T[rt<<1].lazy = T[rt<<1|1].lazy = t;
    T[rt].lazy = 0;
}

void Build(int L, int R, int rt)
{
    if(L == R)
    {
        scanf("%d", &T[rt].sum);
        T[rt].Min = T[rt].Max = T[rt].sum;
        return ;
    }
    int mid = (L + R) >> 1;
    Build(Lson);
    Build(Rson);
    PushUp(rt);
}

void Update(int l, int r, int v, int L, int R, int rt)
{
    if(l==L && r==R)//修改区间值
    {
        T[rt].lazy = v;
        T[rt].sum = v * (R - L + 1);
        T[rt].Min = T[rt].Max = v;
        return ;
    }
    int mid = (L + R) >> 1;
    if(T[rt].lazy) PushDown(L, R, rt);//向下更新一级
    if(r <= mid) Update(l, r, v, Lson);
    else if(l > mid) Update(l, r, v, Rson);
    else
    {
        Update(l, mid, v, Lson);
        Update(mid+1, r, v, Rson);
    }
    PushUp(rt);
}

int Query(int l, int r, int L, int R, int rt)
{
    if(l==L && r== R)
        {
            printf("(%d, %d)---Min: %d   Max: %d  Sum: %d  \n", L, R, T[rt].Min, T[rt].Max, T[rt].sum);
            return T[rt].sum;
        }
    int mid = (L + R) >> 1;
    if(T[rt].lazy) PushDown(L, R, rt);
    if(r <= mid) return Query(l, r, Lson);
    else if(l > mid) return Query(l, r, Rson);
    return Query(l, mid, Lson) + Query(mid + 1, r, Rson);
}

int main()
{
    int n, q;
    scanf("%d", &n);
    Build(1, n, 1);
    scanf("%d", &q);
    int a, b, c, d;
    while(q--)
    {
        scanf("%d%d%d", &a, &b, &c);
        if(a)
        {
            scanf("%d", &d);
            Update(b, c, d, 1, n, 1);
        }
        else printf("%d\n", Query(b, c, 1, n, 1));
    }
    return 0;
}

/*

6
1 2 3 4 5 6
3
0 1 4
1 2 3 0
0 1 4

*/

7.多校一道有意思的题

Problem v

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define lson L, mid, rt<<1
#define rson mid+1, R, rt<<1|1
#define maxn 100000 + 10

long long a[maxn];
int n, m;

struct Node
{
    long long M[2][8];
}T[maxn<<2];

void pushup(int rt)
{
    int l = rt<<1, r = rt<<1|1;
    for(int i=0; i<2; i++)
    for(int j=0; j<2; j++)
    {
        T[rt].M[i][j] = max(max(T[l].M[i][j], T[r].M[i][j]), max(T[l].M[i][0]+T[r].M[1][j], T[l].M[i][9]+T[r].M[0][j]));
    }
}

void build(int L, int R, int rt)
{
    if(L == R)
    {
        scanf("%I64d", &a[L]);
        if(L & 1)
        {
            T[rt].M[1][10] = a[L];
            T[rt].M[0][0] = -INF;
        }
        else
        {
            T[rt].M[0][0] = a[L];
            T[rt].M[1][11] = -INF;
        }
            T[rt].M[1][0] = -INF;
            T[rt].M[0][12] = -INF;
        return ;
    }
    int mid = (L + R) >> 1;
    build(lson);
    build(rson);
    pushup(rt);
}

void update(int t, int v, int L, int R, int rt)
{
    if(L == R)
    {
        if(L & 1)
            T[rt].M[1][13] = v;
        else
            T[rt].M[0][0] = v;
        return ;
    }
    int mid = (L + R) >> 1;
    if(t <= mid) update(t, v, lson);
    else update(t, v, rson);
    pushup(rt);

}

Node query(int l, int r, int L, int R, int rt)
{
    Node tmp;
    if(l == L && r == R)
    {
        tmp = T[rt];
        return tmp;
    }
    int mid = (L + R) >> 1;
    if(r <= mid) return query(l, r, lson);
    else if(l > mid) return query(l, r, rson);
    else
    {
        Node ltmp = query(l, mid, lson);
        Node rtmp = query(mid+1, r, rson);
        for(int i=0; i<2; i++)
        for(int j=0; j<2; j++)
        {
            tmp.M[i][j] = max(max(ltmp.M[i][j], rtmp.M[i][j]), max(ltmp.M[i][0]+rtmp.M[1][j], ltmp.M[i][14]+rtmp.M[0][j]));
        }
        return tmp;
    }
}

int main()
{
    int kase;
    scanf("%d", &kase);
    while(kase--)
    {
        scanf("%d%d", &n, &m);
        build(1, n, 1);

        for(int i=0; iint t, a, b;
            scanf("%d%d%d", &t, &a, &b);
            if(t == 0)
            {
                long long ans = -INF;
                Node tmp = query(a, b, 1, n, 1);
                for(int i=0; i<2; i++)
                for(int j=0; j<2; j++)
                {
                    ans = max(ans, tmp.M[i][j]);
                }
                printf("%I64d\n", ans);
            }
            else
            {
                update(a, b, 1, n, 1);
            }
        }
    }
}

8. 线段树训练题单

  • 1
  • 2
  • 3 (很重要的题单)

你可能感兴趣的:(Summary)