以牛客网的小白赛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
",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
",add);
}
return 0;
}