线段树区间更新--hdu3577

改版的线段树题


本来是花了将近一个钟理解了一次线段树区间更新的版再加上自己把之前线段树版改了下,然后就打算随便找一道区间更新的题套个版看看自己的版对不对,结果套了这道改版的题,样例过了之后妥妥的交了一发,结果wa了,然后又改了点无关紧要的bug,结果又wa了。。然后就套了几个数据把版打出来看。。结果发现我用区间求和的方式把子节点的所有值都加了起来,但是他不需要所有子节点的和啊!!!然后马上又改了下版,一交,又wa了。。。郁闷了很久,感觉我敲的版没错啊。。然后默默的看向了push_up。。好吧这里写错了。。这里还是写成了区间求和。。所以说不是所有版都能乱用的。即使是自己敲出来的。也要更加深刻的去理解里面的意义。不然套版都不会。。。


hdu可用网址
hdu暂时不能用网址


下面先给出hdu3577 ac代码,再给出线段树区间更新的版


/****************
create by QuanQ
create time 2016-9-2
example hdu3577
algorithm 线段树区间更新 
*****************/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;

#define maxn 1000005
#define ll long long

struct SegTree{   //线段树
    int l,r,v;    //lr左右孩子,v值
    int add;      //用来记录还没有更新的值 
}tree[maxn << 2];

/*更新当前父节点的值*/
void push_up(int root){
    tree[root].v = max(tree[root << 1].v,tree[root << 1 | 1].v); //寻找儿子节点最大值
}

/*将父节点中还没有加上的值给儿子节点加上*/
void push_down(int root){
    tree[root << 1].add += tree[root].add;
    tree[root << 1 | 1].add += tree[root].add;
    tree[root << 1].v += tree[root].add;//此处更新当前儿子节点的值 
    tree[root << 1 | 1].v += tree[root].add;
    tree[root].add = 0; 
}


/*创建线段树*/
void build_sTree(int root,int left,int right){
    tree[root].l = left;
    tree[root].r = right;
    tree[root].add = 0;
    tree[root].v = 0;    //总和,初始化为0
    if(left < right){
        int mid = (left + right) >> 1;
        build_sTree(root << 1,left,mid);          //创建左孩子
        build_sTree(root << 1 | 1,mid + 1,right); //创建右孩子
    }
}

/*加入值/更新值*/
void insert_sTree(int value,int root,int left,int right){   //有些代码也叫update,更新值
    /*传入参数:value增加的值,root当前根部--通常传入时为1,need需要更改的key,add新增加的值*/
    if(right < tree[root].l || left > tree[root].r)
        return;
    if(left <= tree[root].l && right >= tree[root].r){
       tree[root].v += value;
       tree[root].add += value;
       return;
    }
    if(tree[root].add)
        push_down(root);
    int mid = (tree[root].l + tree[root].r) >> 1;
    if(right <= mid)
        insert_sTree(value,root << 1,left,right);
    else if(left > mid)
        insert_sTree(value,root << 1 | 1,left,right);
    else{
        insert_sTree(value,root << 1,left,mid);
        insert_sTree(value,root << 1 | 1,mid + 1,right);
    }
    //最后出栈的时候更新数据
    push_up(root);
}

/*查找--区间求和*/
int search_sTree(int root,int l,int r){
    /*传入参数:root当前根部--通常传入时为1,l,r,为所需要从l开始到r的所有数据的区间段,l一定要小于r*/
    int mid = (tree[root].l + tree[root].r) >> 1;
    if(l == tree[root].l && r == tree[root].r)  //如果找到了相应的位置,直接返回值
        return tree[root].v;
    if(tree[root].add) 
        push_down(root);
    if(r <= mid)                                //如果当前搜索的节点范围比正常范围大,则向左移动
        return search_sTree(root << 1,l,r);
    else if(l > mid)                            //如果当前搜索的节点范围比正常范围小,则向右移动
        return search_sTree(root << 1 | 1,l,r);
    else                                        //寻找儿子节点当前人数最大值
        return max(search_sTree(root << 1,l,mid),search_sTree(root << 1 | 1,mid + 1,r));
}
int ans[maxn];
int main(){
    int T,n,value,a,b,k,q;
    scanf("%d",&T);
    for(ll t = 1;t <= T;t++){
        q = 0;
        build_sTree(1,1,maxn);
        scanf("%d %d",&k,&n);
        printf("Case %d:\n",t);
        for(int i = 1;i <= n;i++){
            scanf("%d %d",&a,&b);
            if(search_sTree(1,a,b - 1) < k){
                insert_sTree(1,1,a,b - 1);
                ans[q++] = i;
            }
        }
        for(int i = 0;i < q;i++)
            printf("%d ",ans[i]);
        printf("\n\n");
    }
}
/*
1
3 6
1 6
1 6
3 4
1 5
1 2
2 4
*/

线段树区间更新版


struct SegTree{   //线段树
    int l,r,v;    //lr左右孩子,v值
    int add;      //用来记录还没有更新的值 
}tree[maxn << 2];

/*更新当前父节点的值*/
void push_up(int root){
    tree[root].v = tree[root << 1].v + tree[root << 1 | 1].v;
}

/*将父节点中还没有加上的值给儿子节点加上*/
void push_down(int root){
    tree[root << 1].add += tree[root].add;
    tree[root << 1 | 1].add += tree[root].add;
    tree[root << 1].v += tree[root].add * (tree[root << 1].r - tree[root << 1].l + 1);  //此处更新当前儿子节点的值 
    tree[root << 1 | 1].v += tree[root].add * (tree[root << 1 | 1].r - tree[root << 1 | 1].l + 1);
    tree[root].add = 0; 
}


/*创建线段树*/
void build_sTree(int root,int left,int right){
    tree[root].l = left;
    tree[root].r = right;
    tree[root].add = 0;
    tree[root].v = 0;    //总和,初始化为0
    if(left < right){
        int mid = (left + right) >> 1;
        build_sTree(root << 1,left,mid);          //创建左孩子
        build_sTree(root << 1 | 1,mid + 1,right); //创建右孩子
    }
}

/*加入值/更新值*/
void insert_sTree(int value,int root,int left,int right){   //有些代码也叫update,更新值
    /*传入参数:value增加的值,root当前根部--通常传入时为1,need需要更改的key,add新增加的值*/
    if(right < tree[root].l || left > tree[root].r)
        return;
    if(left <= tree[root].l && right >= tree[root].r){
       tree[root].v += value * (tree[root].r - tree[root].l + 1);
       tree[root].add += value;
       return;
    }
    if(tree[root].add)
        push_down(root);
    int mid = (tree[root].l + tree[root].r) >> 1;
    if(right <= mid)
        insert_sTree(value,root << 1,left,right);
    else if(left > mid)
        insert_sTree(value,root << 1 | 1,left,right);
    else{
        insert_sTree(value,root << 1,left,mid);
        insert_sTree(value,root << 1 | 1,mid + 1,right);
    }
    //最后出栈的时候更新数据
    push_up(root);
}

/*查找--区间求和*/
int search_sTree(int root,int l,int r){
    /*传入参数:root当前根部--通常传入时为1,l,r,为所需要从l开始到r的所有数据的区间段,l一定要小于r*/
    int mid = (tree[root].l + tree[root].r) >> 1;
    if(l == tree[root].l && r == tree[root].r)  //如果找到了相应的位置,直接返回值
        return tree[root].v;
    if(tree[root].add) 
        push_down(root);
    if(r <= mid)                                //如果当前搜索的节点范围比正常范围大,则向左移动
        return search_sTree(root << 1,l,r);
    else if(l > mid)                            //如果当前搜索的节点范围比正常范围小,则向右移动
        return search_sTree(root << 1 | 1,l,r);
    else                                        //如果被当前区间分割,则分开两部分计算
        return search_sTree(root << 1,l,mid) + search_sTree(root << 1 | 1,mid + 1,r);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值