题目链接:https://www.nowcoder.com/acm/contest/135/I
这道题的坑点挺多的,首先要仔细读题,当q等于1的时候要执行操作二,否则执行操作一,然后数据范围也不小,当时用线段树写各种MLE(树写的不够优美),最后发现数字爆int了,但是如果全开long long的话内存也会超内存,所以要合理使用long long...补题的时候用了三种方法过了这道题,树状数组和数组的方法其实是用了差分的思想,维护了一个前缀和。
AC代码(线段树):
#include <iostream>
#include <cstdio>
#include <cstring>
#define lson l, mid, o << 1
#define rson mid + 1, r, o << 1 | 1
#define maxn 1000010
#define ll long long
using namespace std;
int n,m;
int p;
ll pre[maxn << 2],add[maxn << 2];
void Pushup(int o){
pre[o] = pre[o << 1] + pre[o << 1 | 1];
}
void Pushdown(int o, int ans){
if(add[o]){
add[o << 1] += add[o];
add[o << 1 | 1] += add[o];
pre[o << 1] += add[o] * (ans - (ans >> 1));
pre[o << 1 | 1] += add[o] * (ans >> 1);
add[o] = 0;
}
}
void Build(int l,int r,int o){
if(l == r){
scanf("%lld",&pre[o]);
return ;
}
ll mid = (l + r) >> 1;
Build(lson);
Build(rson);
Pushup(o);
}
void Update(int L,int R,int ans,int l,int r,int o){
if(L <= l && r <= R){
pre[o] += (ll)ans * (r - l + 1); // 注意这里要强制转换成ll
add[o] += ans;
return ;
}
Pushdown(o,r-l+1);
int mid = (l + r) >> 1;
if(L <= mid)Update(L,R,ans,lson);
if(R > mid)Update(L,R,ans,rson);
Pushup(o);
}
ll Query(int L,int R,int l,int r,int o){
if(L <= l && r <= R){
return pre[o];
}
Pushdown(o,r-l+1);
int mid = (l + r) >> 1;
ll ans = 0;
if(L <= mid)ans += Query(L,R,lson);
if(R > mid)ans += Query(L,R,rson);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
Build(1,n,1);
while(m--){
scanf("%d",&p);
if(p == 1){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
Update(x,y,-z,1,n,1);
}
else{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
Update(x,y,z,1,n,1);
}
}
int l,r;
scanf("%d%d",&l,&r);
printf("%lld\n",Query(l,r,1,n,1));
return 0;
}
AC代码(数组):
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 1000005
#define ll long long
using namespace std;
int pre[maxn],sum[maxn];
int n,m,q;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&pre[i]);
}
memset(sum,0,sizeof(sum));
while(m--){
scanf("%d",&q);
if(q == 1){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
sum[x] -= z;
sum[y+1] += z;
}
else{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
sum[x] += z;
sum[y + 1] -= z;
}
}
for(int i=1;i<=n;i++){
sum[i] += sum[i-1];
}
int l, r;
ll ans = 0;
scanf("%d%d",&l,&r);
for(int i=l;i<=r;i++){
ans += pre[i] + sum[i];
}
printf("%lld\n",ans);
return 0;
}
树状数组的写法可能不是很好理解,可以按照每一步的数组的变化来模拟一下。
AC代码(树状数组):
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
#define maxn 1000005
using namespace std;
int pre[maxn];
int n,m,q;
int lowbit(int x){return x & (-x);}
void Add(int x,int y){
for(int i=x;i<=n;i+=lowbit(i)){
pre[i] += y;
}
}
int Query(int x){
int sum = 0;
for(int i=x;i>=1;i-=lowbit(i)){
sum += pre[i];
}
return sum;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int index;
scanf("%d",&index);
Add(i, index);
Add(i+1, -index);
}
while(m--){
scanf("%d",&q);
if(q == 1){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
Add(x, -z);
Add(y + 1, z);
}
else{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
Add(x, z);
Add(y + 1, -z);
}
}
int l , r;
scanf("%d%d",&l,&r);
ll ans = 0; // 要开ll
for(int i=l;i<=r;i++){
ans += Query(i);
}
printf("%lld\n",ans);
return 0;
}