线段树

本文深入浅出地讲解了线段树的构造、更新、查询等核心概念,通过实例演示如何利用离散化和线段树解决实际问题。包括动态数据结构的实现、静态全局数组模拟、点树结构体创建、值更新和查找函数用法,以及针对不同题型的解题策略。此外,还提供了几种不同类型的题型实例解答,帮助读者掌握线段树的应用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HH神的线段树出神入化,所以跟着HH学习线段树。

风格:

maxn是题目给的最大区间,而节点数要开4倍,确切的说……

lson和rson辨别表示结点的左孩子和右孩子。

PushUp(int rt)是把当前结点的信息更新到父节点

PushDown(int rt)是把当前结点的信息更新给孩子结点。

rt表示当前子树的根(root),也就是当前所在的结点。


思想:

对于每个非叶节点所标示的结点 [a,b],其做孩子表示的区间是[a,(a+b)/2],其右孩子表示[(a+b)/2,b].

构造:



离散化和线段树:

题目:x轴上有若干个线段,求线段覆盖的总长度。

普通解法:设置坐标范围[min,max],初始化为0,然后每一段分别染色为1,最后统计1的个数,适用于线段数目少,区间范围小。

离散化的解法:离散化就是一一映射的关系,即将一个大坐标和小坐标进行一一映射,适用于线段数目少,区间范围大。

例如:[10000,22000],[30300,55000],[44000,60000],[55000,60000].

第一步:排序 10000 22000 30300 44000 55000 60000

第二部:编号 1 2 3 4 5 6

第三部:用编号来代替原数,即小数代大数 。

[10000,22000]~[1,2]

[30300,55000]~[3,5]

[44000,60000]~[4,6]

[55000,60000]~[5,6]

然后再用小数进行普通解法的步骤,最后代换回去。

线段树的解法:线段树通过建立线段,将原来染色O(n)的复杂度减小到 log(n),适用于线段数目多,区间范围小的情况。

离散化的线段树:适用于线段数目多,区间范围大的情况。


构造:

动态数据结构:

struct node{

node* left;

node* right;

……

}

静态全局数组模拟(完全二叉树):

struct node{

int left;

int right;

……

}Tree[MAXN]

例如:



线段树与点树:

线段树的每一个结点表示一个点,成为点树,比如说用于求第k小数的线段树。

点树结构体:

struct node{

int l, r;

int c;//用于存放次结点的值,默认为0

}T[3*MAXN];

创建:

创建顺序为先序遍历,即先构造根节点,再构造左孩子,再构造右孩子。

  1. voidconstruct(intl,intr,intk){
  2. T[k].l=l;
  3. T[k].r=r;
  4. T[k].c=0;
  5. if(l==r)return;
  6. intm=(l+r)>>1;
  7. construct(l,m,k<<1);
  8. construct(m+1,r,(k<<1)+1);
  9. return;
  10. }
void construct(int l, int r, int k){
    T[k].l = l;
    T[k].r = r;
    T[k].c = 0;
    if(l == r) return ;
    int m = (l + r) >> 1;
    construct(l, m, k << 1);
    construct(m + 1, r, (k << 1) + 1);
    return ;
}



[A,B,C]:A表示左值,B表示右值,C表示在静态数组中的位置,由此可知,n个点的话大约共有2*n个结点,因此开3*n的结构体一定是够的。


更新值:

  1. voidinsert(intd,intk){
  2. //如果找到了就c值+1返回。
  3. if(T[k].l==T[k].r&&d==T[k].l){
  4. T[k].c+=1;
  5. return;
  6. }
  7. intm=(T[k].l+T[k].r)>>1;
  8. if(d<=m)insert(d,k<<1);
  9. elseinsert(d,(k<<1)+1);
  10. //更新每一个c,向上更新
  11. T[k].c=T[k<<1].c+T[(k<<1)+1].c;
  12. }
void insert(int d, int k){
    //如果找到了就c值+1返回。
    if(T[k].l == T[k].r && d == T[k].l){
        T[k].c += 1;
        return ;
    }
    int m = (T[k].l + T[k].r) >> 1;
    if(d <= m) insert(d, k << 1);
    else insert(d, (k << 1) + 1);
    //更新每一个c,向上更新
    T[k].c = T[k << 1].c + T[(k << 1) + 1].c;
}

查找值:

  1. //k表示树根,d表示要查找的值
  2. voidsearch(intd,intk,int&ans)
  3. {
  4. if(T[k].l==T[k].r){
  5. ans=T[k].l;
  6. ans=T[k].l;
  7. }
  8. intm=(T[k].l+T[k].r)>>1;
  9. //不懂
  10. if(d>T[(k<<1)].c)search(d-T[k<<1].c,(k<<1)+1,ans);
  11. elsesearch(d,k<<1,ans);
  12. }
//k表示树根,d表示要查找的值
void search(int d, int k, int& ans)
{
    if(T[k].l == T[k].r){
        ans = T[k].l;
        ans = T[k].l;
    }
    int m = (T[k].l + T[k].r) >> 1;
    //不懂
    if(d > T[(k << 1)].c) search(d - T[k << 1].c, (k << 1) + 1, ans);
    else search(d, k << 1, ans);
}

search函数的用法不太懂。

例题解:

(待更新)


四类题型:

1.单点更新 只更新叶子结点,然后把信息用PushUp(int r)这个函数更新上来。

hdu1166:敌兵布阵

线段树功能:update:单点替换 query:区间最值




poj2828

树状数组:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<string>
  4. #include<cstring>
  5. usingnamespacestd;
  6. typedefpair<int,int>PII;
  7. constintmaxn=200000;
  8. intC[maxn+100];
  9. intB[maxn+100];
  10. intn;
  11. PIIarr[maxn+100];
  12. intlowbit(intk){returnk&(-k);}
  13. voidinit(){
  14. for(inti=1;i<=n;i++)C[i]=lowbit(i);
  15. memset(B,-1,n+10);
  16. }
  17. voidupdate(inti){
  18. while(i<=n){
  19. C[i]--;
  20. i+=lowbit(i);
  21. }
  22. }
  23. intquery(inti){
  24. intret=0;
  25. while(i>0){
  26. ret+=C[i];
  27. i-=lowbit(i);
  28. }
  29. returnret;
  30. }
  31. voiddebug(){
  32. for(inti=1;i<=n;i++)cout<<i<<""<<query(i)<<endl;
  33. }
  34. voidfun(inta,intv){
  35. intl=1,r=n;
  36. while(l<r){
  37. intm=(l+r)>>1;
  38. if(query(m)>=a)r=m;
  39. elsel=m+1;
  40. }
  41. //cout<<"here"<<l<<endl;
  42. update(l);
  43. //cout<<"here2"<<endl;
  44. //debug();
  45. B[l]=v;
  46. //returnl;
  47. }
  48. intmain(){
  49. while(~scanf("%d",&n)){
  50. init();
  51. inta,b;
  52. for(inti=1;i<=n;i++){
  53. scanf("%d%d",&a,&b);
  54. a++;
  55. arr[i].first=a;
  56. arr[i].second=b;
  57. }
  58. for(inti=n;i>0;i--)fun(arr[i].first,arr[i].second);
  59. //debug2();
  60. //boolflag=false;
  61. for(inti=1;i<=n;i++){
  62. i==1?printf("%d",B[i]):printf("%d",B[i]);
  63. //if(B[i]!=-1&&!flag){printf("%d",B[i]);flag=true;}
  64. //elseif(B[i]!=-1)printf("%d",B[i]);
  65. }
  66. puts("");
  67. }
  68. return0;
  69. }
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;

typedef pair<int, int> PII;

const int maxn = 200000;

int C[maxn + 100];
int B[maxn + 100];
int n;
PII arr[maxn + 100];

int lowbit(int k) { return k & (-k); }

void init() {
    for(int i = 1; i <= n; i++) C[i] = lowbit(i);
    memset(B, -1, n + 10);
}

void update(int i) {
    while(i <= n) {
        C[i]--;
        i += lowbit(i);
    }
}


int query(int i) {
    int ret = 0;
    while(i > 0) {
        ret += C[i];
        i -= lowbit(i);
    }
    return ret;
}

void debug() {
    for(int i = 1; i <= n; i++) cout << i << " " << query(i) << endl;
}


void fun(int a, int v) {
    int l = 1, r = n;
    while(l < r) {
        int m = (l + r) >> 1;
        if(query(m) >= a) r = m;
        else l = m + 1;
    }
    //cout << "here  " << l << endl;
    update(l);
    //cout << "here2 " << endl;
    //debug();
    B[l] = v;
    //return l;
}




int main() {
    while(~scanf("%d", &n)) {
        init();
        int a, b;
        for(int i = 1; i <= n; i++) {
            scanf("%d%d", &a, &b);
            a++;
            arr[i].first = a;
            arr[i].second = b;
        }
        for(int i = n; i > 0; i--) fun(arr[i].first, arr[i].second);
        //debug2();
        //bool flag = false;
        for(int i = 1; i <= n; i++) {
            i == 1 ? printf("%d", B[i]) : printf(" %d", B[i]);
            //if(B[i] != -1 && !flag) { printf("%d", B[i]); flag = true; }
            //else if(B[i] != -1) printf(" %d", B[i]);
        }
        puts("");
    }
    return 0;
}

poj-3468

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. usingnamespacestd;
  5. #definelsonl,m,rt<<1
  6. #definersonm+1,r,rt<<1|1
  7. typedeflonglongLL;
  8. constintmaxn=111111;
  9. LLcol[maxn<<2];
  10. LLsum[maxn<<2];
  11. voidPushUp(LLrt){
  12. sum[rt]=sum[rt<<1]+sum[rt<<1|1];
  13. }
  14. //pushdown的作用是如果此点可以更新。
  15. //也就是更新到下一层
  16. //如果是底层,那么是不用pushdown的。
  17. voidPushDown(LLrt,LLm){
  18. if(col[rt]){
  19. //col[rt<<1]=col[rt<<1|1]=col[rt];
  20. col[rt<<1]+=col[rt];
  21. col[rt<<1|1]+=col[rt];
  22. sum[rt<<1]+=col[rt]*(m-(m>>1));
  23. sum[rt<<1|1]+=col[rt]*(m>>1);
  24. col[rt]=0;
  25. }
  26. }
  27. voidbuild(LLl,LLr,LLrt){
  28. col[rt]=0;
  29. //cout<<l<<""<<r<<endl;
  30. if(l==r){
  31. scanf("%I64d",&sum[rt]);
  32. //cout<<rt<<""<<sum[rt]<<endl;
  33. return;
  34. }
  35. intm=(l+r)>>1;
  36. build(lson);
  37. build(rson);
  38. PushUp(rt);
  39. }
  40. LLquery(LLL,LLR,LLl,LLr,LLrt){
  41. LLret=0;
  42. if(L<=l&&r<=R){
  43. //if(col[rt])returnsum[rt]+(r-l+1)*col[rt];
  44. returnsum[rt];
  45. }
  46. PushDown(rt,r-l+1);
  47. intm=(l+r)>>1;
  48. if(L<=m)ret+=query(L,R,lson);
  49. if(R>m)ret+=query(L,R,rson);
  50. returnret;
  51. }
  52. voidupdate(LLL,LLR,LLc,LLl,LLr,LLrt){
  53. if(L<=l&&r<=R){
  54. sum[rt]+=c*(r-l+1);
  55. col[rt]+=c;//子节点没有更新
  56. return;
  57. }
  58. PushDown(rt,r-l+1);
  59. intm=(l+r)>>1;
  60. if(L<=m)update(L,R,c,lson);
  61. if(R>m)update(L,R,c,rson);
  62. PushUp(rt);
  63. }
  64. voiddebug(intn){
  65. for(inti=1;i<=(n*3);i++){
  66. cout<<i<<"";
  67. }
  68. cout<<endl;
  69. for(inti=1;i<=(n*3);i++){
  70. cout<<col[i]<<"";
  71. }
  72. cout<<endl<<endl;
  73. for(inti=1;i<=(n*3);i++){
  74. cout<<i<<"";
  75. }
  76. cout<<endl;
  77. for(inti=1;i<=(n*3);i++){
  78. cout<<sum[i]<<"";
  79. }
  80. cout<<endl;
  81. }
  82. intmain(){
  83. LLN,Q;
  84. while(~scanf("%I64d%I64d",&N,&Q)){
  85. //cout<<"N="<<N<<endl;
  86. memset(sum,0,sizeof(sum));
  87. memset(col,0,sizeof(col));
  88. build(1,N,1);
  89. //debug(N);
  90. for(inti=0;i<Q;i++){
  91. charch[3];
  92. LLa,b,c;
  93. scanf("%s",ch);
  94. if(ch[0]=='Q'){
  95. scanf("%I64d%I64d",&a,&b);
  96. printf("%I64d\n",query(a,b,1,N,1));
  97. }
  98. else{
  99. scanf("%I64d%I64d%I64d",&a,&b,&c);
  100. update(a,b,c,1,N,1);
  101. }
  102. //debug(N);
  103. }
  104. }
  105. return0;
  106. }
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

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

typedef long long LL;

const int maxn = 111111;

LL col[maxn<<2];
LL sum[maxn<<2];

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

//pushdown的作用是如果此点可以更新。
//也就是更新到下一层
//如果是底层,那么是不用pushdown的。
void PushDown(LL rt, LL m) {
    if(col[rt]) {
        //col[rt<<1] = col[rt<<1|1] = col[rt];
        col[rt<<1] += col[rt];
        col[rt<<1|1] += col[rt];
        sum[rt<<1] += col[rt] * (m - (m>>1));
        sum[rt<<1|1] += col[rt] * (m>>1);
        col[rt] = 0;
    }
}

void build(LL l, LL r, LL rt) {
    col[rt] = 0;
    //cout << l << " " << r << endl;
    if(l == r) {
        scanf("%I64d", &sum[rt]);
        //cout << rt << " " << sum[rt] << endl;
        return ;
    }
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUp(rt);
}

LL query(LL L, LL R, LL l, LL r, LL rt) {
    LL ret = 0;
    if(L <= l && r <= R) {
        //if(col[rt]) return sum[rt] + (r - l + 1) * col[rt];
        return sum[rt];
    }
    PushDown(rt, r - l + 1);
    int m = (l + r) >> 1;
    if(L <= m) ret += query(L, R, lson);
    if(R > m) ret += query(L, R, rson);
    return ret;
}

void update(LL L, LL R, LL c, LL l, LL r, LL rt) {
    if(L <= l && r <= R) {
        sum[rt] += c * (r - l + 1);
        col[rt] += c;//子节点没有更新
        return ;
    }
    PushDown(rt, r - l + 1);
    int m = (l + r) >> 1;
    if(L <= m) update(L, R, c, lson);
    if(R > m) update(L, R, c, rson);
    PushUp(rt);
}

void debug(int n) {
    for(int i = 1; i <= (n*3); i++) {
        cout << i << " ";
    }
    cout << endl;
    for(int i = 1; i <= (n*3); i++) {
        cout << col[i] << " ";
    }
    cout << endl << endl;
    for(int i = 1; i <= (n*3); i++) {
        cout << i << " ";
    }
    cout << endl;
    for(int i = 1; i <= (n*3); i++) {
        cout << sum[i] << " ";
    }
    cout << endl;
}

int main() {
    LL N, Q;
    while(~scanf("%I64d%I64d", &N, &Q)) {
        //cout << "N = " << N << endl;
        memset(sum, 0, sizeof(sum));
        memset(col, 0, sizeof(col));
        build(1, N, 1);
        //debug(N);
        for(int i = 0; i < Q; i++) {
            char ch[3];
            LL a, b, c;
            scanf("%s", ch);
            if(ch[0] == 'Q') {
                scanf("%I64d%I64d", &a, &b);
                printf("%I64d\n", query(a, b, 1, N, 1));
            }
            else {
                scanf("%I64d%I64d%I64d", &a, &b, &c);
                update(a, b, c, 1, N, 1);
            }
            //debug(N);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值