天天看點

牛客網 小白賽5 I區間 線段樹,差分思想,樹狀數組

題目連結:​​https://www.nowcoder.com/acm/contest/135/I​​

好像是區間修改查詢,想到了線段樹,但是空間要開四倍,然後隻過了百分之三十的資料,嗚嗚嗚,線段樹也是可以空間優化的

有興趣的同學可以研究下

空間優化

父節點k,左二子2*k,右兒子2*k+1,需要4*n的空間

但并不是所有的葉子節點占用到2n+1——4n

這就造成大量空間浪費

2*n空間表示法:推薦部落格:​​http://www.cppblog.com/MatoNo1/archive/2015/05/05/195857.html​​

用dfs序表示做節點下标

父節點k,左兒子k+1,右兒子:k+左兒子區間長度*2,不是父節點下标+父節點區間長度。因為當樹不滿時,兩者不相等

具體實作這裡就不再寫模闆了,就是改改左右兒子的下标

可參考代碼: 題目:樓房重建

裡面的建樹用的2*n空間

線段樹代碼是這樣的

#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;
}      

然後就想用樹狀數組:

從同學那裡借鑒的代碼是這樣的:

#include<bits/stdc++.h>
using namespace std;
  
typedef long long ll;
const int maxn=1000000+100;
  
ll bit[maxn][2];
int ans[maxn];
  
int n,m;
 
template<typename Q>
 
inline void inin(Q &x)
{
    x=0;
    int f=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
         
       if(ch=='-')f=1;ch=getchar();
    }
    while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();
    x=f?-x:x;
}
  
inline int lowbit(int x){
      
    return x&(-x);
}
  
inline void Add(int x,ll v,int ind){
      
    while(x<=n){
          
        bit[x][ind]+=v;
        x+=lowbit(x);
    }
}
  
inline ll getSum(int x,int ind){
      
    ll sum=0;
    while(x>0){
          
        sum+=bit[x][ind];
        x-=lowbit(x);
    }
    return sum;
}
  
int main(){
      
    inin(n);
    inin(m);
    for(int i=1;i<=n;i++){
          
        inin(ans[i]);
    }
    for(int i=1;i<=m;i++){
          
        int q,l,r,p;
        inin(q);
        inin(l);
        inin(r);
        inin(p);
        if(q==1){
              
            p*=-1;
            Add(l,p,0);
            Add(r+1,-p,0);
            Add(l,(ll)l*p,1);
            Add(r+1,(ll)(-r-1)*p,1);
        }
        else{
              
            Add(l,p,0);
            Add(r+1,-p,0);
            Add(l,(ll)l*p,1);
            Add(r+1,(ll)(-r-1)*p,1);
        }
    }
    int l,r;
    inin(l);
    inin(r);
    ll tmp=(r+1)*getSum(r,0)-getSum(r,1);
    tmp-=l*getSum(l-1,0)-getSum(l-1,1);
    for(int i=l;i<=r;i++) tmp+=(ll)ans[i];
    printf("%lld\n",tmp);
}      

最後是差分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template<typename Q>
void inin(Q &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    x=f?-x:x;
}
const ll MAXN=1004567;
ll a[MAXN],v[MAXN];
int main(){
    int n, m, x;
    inin(n),inin(m);
    for (int i = 1; i <= n; ++i) {
        inin(a[i]);
    }
    int l, r, q, i;
    memset(v, 0, sizeof(v));
    for (i = 0; i < m; ++i) {
        inin(q);inin(l);inin(r);inin(x);
        if(q == 1){
            v[l] -= x;
            v[r+1] += x;
        } else{
            v[l] += x;
            v[r+1] -= x;
        }
    }
    inin(l),inin(r);
    ll t = 0;
    ll sum = 0;
    for (i = 1; i <= r; ++i) {
        t += v[i];
        if(i >= l)sum += t + a[i];
    }
    printf("%lld\n", sum);
}