区间修改+区间查询

以牛客网的小白赛5,第i题,为列!

典型的区间修改+区间查询

题目如下:

链接:https://www.nowcoder.com/acm/contest/135/I
来源:牛客网
 

题目描述

    Apojacsleam喜欢数组。

    他现在有一个n个元素的数组a,而他要对a[L]-a[R]进行M次操作:

        操作一:将a[L]-a[R]内的元素都加上P

        操作二:将a[L]-a[R]内的元素都减去P

    最后询问a[l]-a[r]内的元素之和?

    请认真看题干及输入描述。

输入描述:

输入共M+3行:

第一行两个数,n,M,意义如“题目描述”

第二行n个数,描述数组。

第3-M+2行,共M行,每行四个数,q,L,R,P,若q为1则表示执行操作2,否则为执行操作1

第4行,两个正整数l,r

输出描述:

一个正整数,为a[l]-a[r]内的元素之和

 

示例1

输入

复制

10 5
1 2 3 4 5 6 7 8 9 10
1 1 5 5
1 2 3 6
0 2 5 5 
0 2 5 8
1 4 9 6
2 7

输出

复制

23

说明

 

这题目有很多解决的办法,但是要求时间复杂度符合要求。

方法一:

首先,很多acmer想到的肯定是树状数组,这确实很好用,时间复杂度,O(n*log(n))级别,读者可自行了解树状数组的原理及其模板。很好用

代码如下:

#include <cstdio>
#define lowbit(x) (x&-x)
#define ll long long
const ll maxn=1000010;
using namespace std;
ll n, m, q, c1[maxn], c2[maxn], num[maxn];

void add(ll *r, ll pos, ll v) {  //区间加
	for(; pos<=n; pos+=lowbit(pos))r[pos]+=v;
}

void sub(ll *r,ll pos, ll v) {   //区间减
	for(; pos<=n; pos+=lowbit(pos))r[pos]-=v;
}

ll sigma(ll *r, ll pos) {  //求区间和
	ll ans;
	for(ans=0; pos; pos-=lowbit(pos))ans+=r[pos];
	return ans;
}

int main() {
	ll i, j, type, a, b, v, sum1, sum2, q;
	while(scanf("%d%d",&n,&m)==2) {
		for(i=1; i<=n; i++) {  //输入原始数据
			scanf("%lld",num+i);
			add(c1,i,num[i]-num[i-1]);
			add(c2,i,(i-1)*(num[i]-num[i-1]));
		}
		for(int i=0; i<m; i++) {
			scanf("%lld%lld%lld%lld",&q,&a,&b,&v);
			if(q!=1) {  //修改a,b之间,加上v 
				add(c1,a,v);
				add(c1,b+1,-v);
				add(c2,a,v*(a-1));
				add(c2,b+1,-v*b);
			} else {    //修改a,b之间,减去v 
				sub(c1,a,v);
				sub(c1,b+1,-v);
				sub(c2,a,v*(a-1));
				sub(c2,b+1,-v*b);
			}
		}
		scanf("%lld%lld",&a,&b);  //输入要求的a,b区间 
		sum1=(a-1)*sigma(c1,a-1)-sigma(c2,a-1); 
		sum2=b*sigma(c1,b)-sigma(c2,b);
		printf("%lld\n",sum2-sum1);  //a,b前缀和相减 
	}
	return 0;
}

 

方法二:

先把修改的命令都先储存起来,然后再与之最后要求的区间相匹配。

首先,add=还没开始修改的区间和;然后做一下处理

如果修改的区间与要求的区间相重合,则:add+=(重合的长度)*(p)  或   add-=(重合的长度)*(p)

时间复杂度为O(N),很巧妙的方法,当然只限于此题,不通用。

详细ac代码如下:

#include<iostream>
#include<cstring>
using namespace std;
 
const int maxn=1000000+5;
int num[maxn];
int q[maxn],l[maxn],r[maxn],p[maxn];
 
int main() {
    int n,m,a,b;
    while(scanf("%d%d",&n,&m)==2) {
        for(int i=1; i<=n; i++) {
            scanf("%d",&num[i]);
        }
        for(int i=0; i<m; i++) {
            scanf("%d%d%d%d",&q[i],&l[i],&r[i],&p[i]);  //把这个所有输入储存起来 
        }
        unsigned long long add=0;
        scanf("%d%d",&a,&b);  //根据最后确定的区间,只处理这个区间里面的 
        for(int i=a; i<=b; i++)  //先求出a,b区间的和 
            add+=num[i];
        for(int i=0; i<m; i++) {  
            if(q[i]==1) {
                if(l[i]<=r[i])  //注意这很关键,有可能数据l>r,不合法 
                    if(l[i]<a) {   //四种区间重合的情况 
                        if(r[i]>=a&&r[i]<=b) {
                            add-=(r[i]-a+1)*p[i];
                        } else if(r[i]>b) {
                            add-=(b-a+1)*p[i];
                        }
                    } else if(a<=l[i]&&l[i]<=b) {
                        if(a<=r[i]&&r[i]<=b) {
                            add-=(r[i]-l[i]+1)*p[i];
                        } else if(r[i]>b) {
                            add-=(b-l[i]+1)*p[i];
                        }
                    }
            } else {
                if(l[i]<=r[i])
                    if(l[i]<a) {
                        if(r[i]>=a&&r[i]<=b) {
                            add+=(r[i]-a+1)*p[i];
                        } else if(r[i]>b) {
                            add+=(b-a+1)*p[i];
                        }
                    } else if(a<=l[i]&&l[i]<=b) {
                        if(a<=r[i]&&r[i]<=b) {
                            add+=(r[i]-l[i]+1)*p[i];
                        } else if(r[i]>b) {
                            add+=(b-l[i]+1)*p[i];
                        }
                    }
            }
        }
        printf("%lld\n",add);
    }
    return 0;
}

 

posted @ 2018-07-23 11:14  Horken  阅读(391)  评论(0编辑  收藏  举报