zoukankan      html  css  js  c++  java
  • 线段树-题解

    nyoj 1068

    题目链接  http://acm.nyist.net/JudgeOnline/problem.php?pid=1068

    题目意思; 典型的线段树,插线问线.不过多了一个,

    A 操作某一个区间一个数整体加上一个数;

    S 操作查询某一个区间的总和,

    Q 操作,查询这个区间有多少个奇数.

    下面是 线段树延迟更新,奇数的个数更新时注意 

    如果变化的是奇数,那么  : 现在区间奇数个数=区间长度-原本区间的奇数个数.

    下面 延迟更新  模板题目.如果对模板不太理解,请看这个博 或者看代码注释

    延迟更新大致思想: 每一次数据更新,  [a,b] 区间,则只需要更新到他们的子区间且  满足各个子区间无交集 且 并集为区间 [a,b];每次更新,都更新到这些子区间停止,递归归的时候再更新上面的值. 查询的时候mark,标记的是上次更新到这里停止了,如果要往下走,标记下沉,并计算左右儿子.


    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<cmath>
    #include<set>
    #include<map>
    #include<list>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<string>
    #include<vector>
    #include<iostream>
    #include<algorithm>
    #include<stdlib.h>
    #include<time.h>
    
    using namespace std;
    typedef long long LL;
    const int INF=2e9+1e8;
    const int MOD=1e9+7;
    const int MAXSIZE=1e6+5;
    const double eps=0.0000000001;
    void fre()
    {
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    }
    #define memst(a,b) memset(a,b,sizeof(a))
    #define fr(i,a,n) for(int i=a;i<n;i++)
    
    struct Node
    {
        LL odd,sum,mark;
        LL l,r;
    } Tree[10005*4];
    LL arr[MAXSIZE];
    void build(LL l,LL r,LL pos) //建树过程,递归建树
    {
        Tree[pos].mark=0;//初始化标记变量
        Tree[pos].l=l,Tree[pos].r=r;
        LL mid=(l+r)>>1ll;
        if(l==r)
        {
            Tree[pos].sum=arr[l];
            if(arr[l]&1ll) Tree[pos].odd=1ll;
            else Tree[pos].odd=0;
            return ;
        }
        build(l,mid,pos<<1ll);
        build(mid+1ll,r,pos<<1ll|1ll);
        //上面是递的过程,下面是归并且求和;
        Tree[pos].sum=Tree[pos<<1ll].sum+Tree[pos<<1ll|1ll].sum;
        Tree[pos].odd=Tree[pos<<1ll].odd+Tree[pos<<1ll|1ll].odd;
        return ;
    }
    /**   pushdown 函数
    将本节点的左右孩子节点的标记变量加上自己节点的标记值,
    且把计算结果(通常是求和)并把自己的标记值清空
    */
    void pushdown(LL pos)
    {
        if(Tree[pos].mark)
        {
            Tree[pos<<1ll].mark+=Tree[pos].mark,Tree[pos<<1ll|1ll].mark+=Tree[pos].mark;
            Tree[pos<<1ll].sum+=(Tree[pos<<1ll].r-Tree[pos<<1ll].l+1ll)*Tree[pos].mark;
            Tree[pos<<1ll|1ll].sum+=(Tree[pos<<1ll|1ll].r-Tree[pos<<1ll|1ll].l+1ll)*Tree[pos].mark;
            if(Tree[pos].mark&1ll)
            {
                Tree[pos<<1ll].odd=(Tree[pos<<1ll].r-Tree[pos<<1ll].l+1ll)-Tree[pos<<1ll].odd;
                Tree[pos<<1ll|1ll].odd=(Tree[pos<<1ll|1ll].r-Tree[pos<<1ll|1ll].l+1ll)-Tree[pos<<1ll|1ll].odd;
            }
            Tree[pos].mark=0;
        }
    }
    /** update 函数
    插线函数,递归开始;
    如果找到子区间则,更新当前节点,因为只不需要更新到叶子节点,
    所以,计算当前值,把当前节点的标记变量 加上 k,停止递归;
    
    否则接着递归下去,每次需要下沉标记,并计算;
    --> 递归
    合并左右孩子节点的值,
    */
    void update(LL l,LL r,LL pos,LL k)
    {
        LL mid=(Tree[pos].l+Tree[pos].r)>>1ll;
        if(Tree[pos].l==l&&Tree[pos].r==r) //如果更新到子区间
        {
            Tree[pos].mark+=k;//标记变量自加
            //每次更新都活到这里,所以计算的时候是 k ,不是 mark
            Tree[pos].sum+=(Tree[pos].r-Tree[pos].l+1ll)*k; //
            if(k&1ll) Tree[pos].odd=(Tree[pos].r-Tree[pos].l+1ll)-Tree[pos].odd;
            return ;
        }
        pushdown(pos);
        if(mid<l) update(l,r,pos<<1ll|1ll,k);
        else if(r<=mid)update(l,r,pos<<1ll,k);
        else
        {
            update(l,mid,pos<<1ll,k);
            update(mid+1ll,r,pos<<1ll|1ll,k);
        }
        Tree[pos].sum=Tree[pos<<1ll].sum+Tree[pos<<1ll|1ll].sum;
        Tree[pos].odd=Tree[pos<<1ll].odd+Tree[pos<<1ll|1ll].odd;
    }
    void Debug(LL pos)
    {
        printf("l=%d r=%d sum=%d odd=%d mark=%d
    ",Tree[pos].l,Tree[pos].r,Tree[pos].sum,Tree[pos].odd,Tree[pos].mark);
        if(Tree[pos].l==Tree[pos].r) return ;
        Debug(pos<<1ll);
        Debug(pos<<1ll|1ll);
    }
    /**  query 函数-返回答案
    只要查询,并且标记下沉,计算;
    */
    LL query(LL l,LL r,LL pos,char opt)
    {
        LL mid=(Tree[pos].l+Tree[pos].r)>>1ll;
        if(Tree[pos].l==l&&Tree[pos].r==r)
        {
            if(opt=='S') return Tree[pos].sum;
            else return Tree[pos].odd;
        }
        pushdown(pos);
        if(mid<l) return query(l,r,pos<<1ll|1ll,opt);
        else if(r<=mid) return query(l,r,pos<<1ll,opt);
        else return query(l,mid,pos<<1ll,opt)+query(mid+1ll,r,pos<<1ll|1ll,opt);
    }
    int main()
    {
        LL n,m;
        while(scanf("%lld%lld",&n,&m)+1ll)
        {
            LL a,b,c;
            for(LL i=1ll; i<=n; i++) scanf("%lld",&arr[i]);
            build(1ll,n,1ll);
            for(LL i=0; i<m; i++)
            {
                char opt[3];
                scanf("%s",opt);
                if(opt[0]=='A')
                {
                    scanf("%lld%lld%lld",&a,&b,&c);
                    update(a,b,1ll,c);
                    //  Debug(1ll);
                }
                else
                {
                    scanf("%lld%lld",&a,&b);
                    printf("%lld
    ",query(a,b,1ll,opt[0]));
                }
            }
        }
        return 0;
    }
    
    /**************************************************/
    /**             Copyright Notice                 **/
    /**  writer: wurong                              **/
    /**  school: nyist                               **/
    /**  blog  : http://blog.csdn.net/wr_technology  **/
    /**************************************************/
    


  • 相关阅读:
    第三周学习进度总结
    第二周学习进度总结
    动手动脑04
    动手动脑03
    动手动脑02
    课堂实践总结
    课堂实践
    原码,反码和补码学习报告
    开学第一周
    第八周
  • 原文地址:https://www.cnblogs.com/coded-ream/p/7207941.html
Copyright © 2011-2022 走看看