牛客網 小白賽5 I區間 線段樹,差分思想,樹狀陣列
阿新 • • 發佈:2019-02-04
分析:這個題目一看,恩,好像就是區間修改查詢,馬上想到了線段樹,一毛一樣,但是線段樹的時間複雜度為O(nlogn),可能會卡時間,但是題目稍鬆的話不會卡,但是空間要開四倍,所以說很可能空間不夠用,關鍵是題目資料太多,就要開long long ,然後我就只過了百分之三十的資料,嗚嗚嗚,線段樹也是可以空間優化的
有興趣的同學可以研究下
空間優化
父節點k,左二子2*k,右兒子2*k+1,需要4*n的空間
但並不是所有的葉子節點佔用到2n+1——4n
這就造成大量空間浪費
用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);
}