243. 一个简单的整数问题2 (树状数组维护差分数组: 区间修改,区间查询 | 线段树)

给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:

1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。

2、“Q l r”,表示询问 数列中第 l~r 个数的和。

对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数N,M。

第二行N个整数A[i]。

接下来M行表示M条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围

1≤N,M≤1051≤N,M≤105,
|d|≤10000|d|≤10000,
|A[i]|≤1000000000|A[i]|≤1000000000

输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出样例:

4
55
9
15

 

在区间修改,单点查询中,我们用树状数组维护差分数组b[i],差分数组中 i 的前缀和代表着原数组中a[i]在经过诸多指令后所改变的值。若查询 i 经过诸多指令后的值  = =>  原数组 中a[i] + getsum(i)(getsum(i)表示 b[ ]中i的前缀和)

现在这个问题是区间修改,区间查询。我们还是引入差分数组b[ ] 。

那么原数组a [ ] 在经过若干指令后,a[1 ~ x]所增加的值应该是每个1~ x中每个点增加的值的和。

即                \sum_{i = 1}^{x}\sum_{j = 1}^{i} b[j]

上式可以写成

\sum_{i = 1}^{x}\sum_{j = 1}^{i} b[j] = \sum_{i = 1}^{x}(x - i +1)*b[i] = (x+1)\sum _{i = 1}^{x} b[i] - \sum _{i = 1}^{x} i*b[i]

所以在本题中,我们在增加一个差分数组并用树状数组维护 i * b[i]。在增加一个前缀和数组记录原数组的前缀和。

AC Code:

#include
using namespace std;
typedef long long LL;
const LL Size = 100010;
LL  a[Size],n,m;
LL  d1[Size],d2[Size] ;
LL  sum[Size];
LL  lowbit(LL x)
{
    return x&(-x);
}
void add1(LL x,LL num)
{
    for(;x<=n;x += lowbit(x)) d1[x] += num;
}
void add2(LL x,LL num)
{
    for(;x <= n;x += lowbit(x)) d2[x] += num;
}
LL getsum1(LL x){
  LL ans = 0;
  for(;x > 0;x -= lowbit(x)) ans += d1[x];
  return ans;
}
LL getsum2(LL x)
{
    LL ans = 0;
    for(;x > 0;x -= lowbit(x)) ans += d2[x];
    return ans;
}
int  main()
{
    cin>>n>>m;
    for(LL  i = 1;i <= n;++i) cin>>a[i],sum[i] += (sum[i - 1] + a[i]);
    while(m--)
    {
        char op[2];int l,r,d;
        cin>>op>>l>>r;
        if(op[0]=='C'){
            cin>>d;
            add1(l,d);
            add1(r+1,-d);
            add2(l,l*d);
            add2(r+1,-(r+1)*d);
        }
        else {
            LL ans = sum[r]+(r+1)*getsum1(r) - getsum2(r);//若干指令后 1~r的值
            LL res = sum[l-1] + l*getsum1(l-1) - getsum2(l-1);//若干指令后1~l - 1的值
            cout<

一般这种求和问题还是靠线段树啊,lazy标记。

AC  Code:

#include
using namespace std;
typedef long long LL;
const LL N = 100050;
struct Tree
{
	LL l,r;
	LL sum,add;
#define l(id) tree[id].l
#define r(id) tree[id].r
#define sum(id) tree[id].sum
#define add(id)  tree[id].add
} tree[N*4+1];
LL a[N],n,m;
void build(LL id,LL l,LL r)
{
	l(id) = l,r(id) = r;
	if(l == r)
	{
		sum(id) = a[l];
	}
	else
	{
		LL mid = l + r >>1;
		build(id<<1,l,mid);
		build(id<<1|1,mid+1,r);
		sum(id) = sum(id<<1) + sum(id<<1|1);
	}
}
void push_down(LL id)
{
	if(add(id))
	{
		sum(id<<1) += add(id)*(r(id<<1) - l(id<<1) +1);
		sum(id<<1|1) += add(id)*(r(id<<1|1) - l(id<<1|1)+1);
		add(id<<1) += add(id);
		add(id<<1|1) += add(id);
		add(id) = 0;
	}
}
void updata(LL id,LL l,LL r,LL d)
{
	if(l <= l(id) && r>=r(id))
	{
		sum(id) += (LL)d*(r(id) - l(id) +1);
		add(id) += d;
		return ;
	}
	push_down(id);
	LL mid = l(id) + r(id) >>1;
	if(r<= mid) updata(id<<1,l,r,d);
	else if(l>mid) updata(id<<1|1,l,r,d);
	else
	{
		updata(id<<1,l,mid,d);
		updata(id<<1|1,mid+1,r,d);
	}
	sum(id) = sum(id<<1) + sum(id<<1|1);
}
LL query(LL id,LL l,LL r)
{
	if(l <= l(id)&&r >= r(id)) return sum(id);
	push_down(id);
	LL mid =l(id) + r(id) >>1;
	if(r<=mid) return query(id<<1,l,r);
	else if(l>mid) return query(id<<1|1,l,r);
	else return query(id<<1,l,mid) + query(id<<1|1,mid+1,r);
}
int main()
{
	//freopen("1.in","r",stdin);
	cin>>n>>m;
	for(LL i = 1; i<=n; ++i) scanf("%lld",&a[i]);
	build(1,1,n);
	while(m--)
	{
		char op[2];
		LL l,r,d;
		scanf("%s%lld%lld",op,&l,&r);
		if(op[0]=='C')
		{
			scanf("%lld",&d);
			updata(1,l,r,d);
		}
		else printf("%lld\n",query(1,l,r));
	}
}

AC Code:

//ECUST luoyongjun
#include 
using namespace std;
const int MAXN = 1e5 + 10;
long long sum[MAXN << 2], add[MAXN << 2];       //4倍空间
void push_up(int rt){     //向上更新,通过当前结点rt把值递归但父结点
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void push_down(int rt, int m){               //更新rt的子结点
    if(add[rt]){
        add[rt << 1] += add[rt];
        add[rt << 1 | 1] += add[rt];
        sum[rt << 1] += (m - (m >> 1)) * add[rt];
        sum[rt << 1 | 1] += (m >> 1) * add[rt];
        add[rt] = 0;                             //取消本层标记
    }
}
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
void build(int l, int r, int rt){            //用满二叉树建树
   // add[rt] = 0;
    if(l == r){                                   //叶子结点,赋值
        scanf("%lld", &sum[rt]);
        return;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    push_up(rt);                                  //向上更新区间和
}
void update(int a, int b, long long c, int l, int r, int rt){//区间更新
    if(a <= l && b >= r){
        sum[rt] += (r - l + 1) * c;
        add[rt] += c;
        return;
    }
    push_down(rt, r - l + 1);//向下更新
    int mid = (l + r) >> 1;
    if(a <= mid) update(a, b, c, lson);        //分成两半,继续深入
    if(b > mid) update(a, b, c, rson);
    push_up(rt);//向上更新
}
long long query(int a, int b, int l, int r, int rt){       //区间求和
    if(a <= l && b >= r) return sum[rt];          //满足lazy,直接返回值
    push_down(rt, r - l + 1);                       //向下更新
    int mid = (l + r) >> 1;
    long long ans = 0;
    if(a <= mid) ans += query(a, b, lson);
    if(b > mid) ans += query(a, b, rson);
    return ans;
}
int main(void){
    int n, m;
    scanf("%d%d", &n, &m);
    build(1, n, 1);
    while(m--){
        char str[2];
        int a, b; long long c;
        scanf("%s", str);
        if(str[0] == 'C'){
            scanf("%d%d%lld", &a, &b, &c);
            update(a, b, c, 1, n, 1);
        }else{
            scanf("%d%d", &a, &b);
            printf("%lld\n", query(a, b, 1, n, 1));
        }
    }
}

 

你可能感兴趣的:(树状数组,线段树)