树状数组~poj3468~区间修改 区间查询

题目意思为:

给你n组数据,m次操作。(1~100000)

a1 ,a2,.....,an   (-10^9<= a1 <=10^9).

Q X Y :输出X~Y数据之和。

C X Y Z:对X~Y组数据各加Z。


区间修改比点更新多了一个懒惰标记,保证算法的高效性。代码如下:

注意 32位不够用

#include <stdio.h>
#define MAX 100000

struct ac       //线段树结构体
{
    int l,r;
    long long sign;                 //sign为懒惰标记
    long long sum;
}t[4*MAX+5];

void build(int l,int r,int k)   //建树
{
    t[k].l=l;
    t[k].r=r;
    t[k].sign=0;
    t[k].sum=0;
    if(l==r)
        return ;
    int mid=(l+r)/2;
    build(l,mid,2*k);           //左边
    build(mid+1,r,2*k+1);       //右边
}
void  tree_add(long long v,int d,int k)    //输入叶子的值
{
    if(t[k].l==t[k].r && t[k].l==d) //找到叶子,对其进行赋值
    {
        t[k].sum=v;
        return ;
    }
    int mid=(t[k].l+t[k].r)/2;
    if(d<=mid)                      //叶子在左边
        tree_add(v,d,2*k);
    else if(mid+1<=d)               //叶子在右边
        tree_add(v,d,2*k+1);
    t[k].sum=t[2*k].sum+t[2*k+1].sum;  //在添加叶子的同时,把结点的sum值更新
}

void tree_insert(int x,int y,long long c,int k)   //区间修改,先更新懒惰标记再考虑sum
{
    if(t[k].l==x && t[k].r==y)    //若该区间为需要修改的区间
    {
        t[k].sign+=c;              //注意,是懒惰标记加C
        return ;
    }
    if(t[k].l<=x && y<=t[k].r)      //若该区间不是所求区间,则更新sum值,继续往下递归
        t[k].sum+=((y-x+1)*c);
    int mid=(t[k].l+t[k].r)/2;
    if(y<=mid)                          //若修改的区间全部在左边
        tree_insert(x,y,c,2*k);
    else if(mid+1<=x)                   //若修改的区间全部在右边
        tree_insert(x,y,c,2*k+1);
    else                                //修改的区间在两边
    {
        tree_insert(x,mid,c,2*k);
        tree_insert(mid+1,y,c,2*k+1);
    }
}

long long  research(int x,int y,int k)         //查找x~y的值之和
{
   // printf("!!!!%lld \n",t[k].sum);
    if(t[k].l==x && y==t[k].r)              //查找区间在该节点区间内
        return t[k].sum+(y-x+1)*t[k].sign;  //注意,区间和由sum sign共同决定

    if(t[k].sign)         //若有懒惰标记
    {
        t[2*k].sign+=t[k].sign; //把懒惰赋值赋予下两个节点的懒惰标记
        t[2*k+1].sign+=t[k].sign;
        t[k].sum+=((t[k].r-t[k].l+1)*t[k].sign);    //更新当前节点sum值
        t[k].sign=0;         //当前sum以为l~r区间之和,除去懒惰标记。
    }

    int mid=(t[k].l+t[k].r)/2;
    if(y<=mid)                          //查找区间在左边
        return research(x,y,2*k);       //查找区间在右边
    else if(mid+1<=x)
        return research(x,y,2*k+1);
    else                                //查找区间在两边
        return  research(x,mid,2*k)+research(mid+1,y,2*k+1);
}

void put(int k)
{
    printf("k:%d  sign:%lld  sum:%lld\n",k,t[k].sign,t[k].sum);
    if(t[k].l==t[k].r)
        return ;
    put(2*k);
    put(2*k+1);
}
int main()
{
    int n,q;
    while(scanf("%d %d",&n,&q)!=EOF)
    {
        build(1,n,1);
        int i;
        long long  v;
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&v);
            tree_add(v,i,1);
        }
    //    put(1);

        char s[10],w;
        int x,y;
        long long  c;
        while(q--)
        {
            scanf("%s",s);
            if(s[0]=='Q')
            {
                scanf("%d %d",&x,&y);
                printf("%lld\n",research(x,y,1));
            }
            else if(s[0]=='C')
            {
                scanf("%d %d %lld",&x,&y,&c);
                tree_insert(x,y,c,1);
            }
        }
    //    put(1);

    }
    return 0;
}
线段树区间修改,最主要的代码是:

1.懒惰标记sign的加入,

2.sum,sign的更新,

3。通过sum,sign得出区间数值之和。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值