牛客小白月赛5 I.区间(interval) (前缀和+树状数组+线段树)

题目链接: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;
}

猜你喜欢

转载自blog.csdn.net/Charles_Zaqdt/article/details/81180419