前言
做個總結,忘記之後再翻翻
首先明確一下樹狀數組的結構性質:
1.每個內部節點保存以它爲根的子樹中所有葉節點的和
2.每個內部節點的子節點個數等於的位數,即位爲1的數量
例如:
,分爲兩個區間,區間和分別爲
,分爲三個區間,區間和分別爲
3.除樹根外,每個內部節點的父節點是
4.樹的深度爲。因此可以保證在的時間內查詢前綴和
樹狀數組應用總結
主要分爲三種:
- 單點修改,單點查詢(最基礎)
- 區間修改,單點查詢
- 區間修改,區間查詢
應用一:單點修改,單點查詢
最基礎的定義
查詢
int ask(int x)
{
int ans=0;
for (;x;x-=x&-x)ans+=c[x];
return ans;
}
修改
void add(int x,int y)
{
for (;x<=n;x+=x&-x)c[x]+=y;
}
例題:POJ2182
題意:有頭奶牛,身高不等,站成一列,已知第頭奶牛前面有頭比他矮,求出所有身高。
思路:從後往前思考。表示前頭牛有頭比最後一頭矮,由於不存在重複身高,最後一頭牛的身高肯定是排第位的,同理第頭的身高一定是排第位,但是已經出現過的身高就得排除。可以通過維護一個初始全1的01序列,查詢第個1的位置並且將其修改爲0。用二分+樹狀數組即可
代碼:
int n,A[maxn],c[maxn],ans[maxn];
int ask(int x){int ans=0;for(;x;x-=x&-x)ans+=c[x];return ans;}
void add(int x,int y){for(;x<=n;x+=x&-x)c[x]+=y;}
int solve(int x)
{
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if (ask(mid)<x)l=mid+1;
else r=mid;
}
return l;
}
int main()
{
scanf("%d",&n);
A[1]=0;rep(i,2,n)scanf("%d",&A[i]);
rep(i,1,n)add(i,1);
for (int i=n;i>=1;i--)
{
ans[i]=solve(A[i]+1);
add(ans[i],-1);
}
rep(i,1,n)W(ans[i]);
}
應用二:區間修改,單點查詢
新建數組,前綴和~反映了指令"C l r d"對的影響
1.把
2.把
觀察一下數組的變化:
1.當時:前綴和~無變化
2.當時:前綴和~增加了,因爲增加了
3.當時:前綴和~,因爲一加一減剛好抵消
也就是說 對於區間內的數的影響,都體現在了數組的前綴和中
而求前綴和就是樹狀數組的基礎操作,累加上原先的即可得到"Q x"的答案
藍書例題:
代碼:
int n,m,l,r,x,a[maxn],c[maxn];
char s[5];
int ask(int x){int ans=0;for (;x;x-=x&-x)ans+=c[x];return ans;}
void add(int x,int y){for (;x<=n;x+=x&-x)c[x]+=y;}
int main()
{
scanf("%d%d",&n,&m);
rep(i,1,n)scanf("%d",&a[i]);
while(m--)
{
scanf("%s",s);
if (s[0]=='Q')
{
scanf("%d",&x);
W(ask(x)+a[x]);
}
else if (s[0]=='C')
{
scanf("%d%d%d",&l,&r,&x);
add(l,x);
add(r+1,-x);
}
}
}
應用三:區間修改,區間查詢
首先仍是維護一個數組,變化量就是前綴和~
那麼原數組的前綴和~變化量就是:
第一部分就是維護的前綴和
第二部分就是維護的前綴和,令
由於只是乘了個,故與之前對應的操作是:
1.把
2.把
觀察一下數組的變化:
當時:前綴和~增加了,因爲增加了
綜上所述:的原始前綴和+的前綴和變化量即爲最終的的前綴和
區間查詢即輸出
藍書例題:
代碼:
int n,m;
ll a[maxn],c1[maxn],c2[maxn],sum[maxn],l,r,x;
char s[5];
ll ask1(ll x){ll ans=0;for(;x;x-=x&-x)ans+=c1[x];return ans;}
ll ask2(ll x){ll ans=0;for(;x;x-=x&-x)ans+=c2[x];return ans;}
void add1(ll x,ll y){for(;x<=n;x+=x&-x)c1[x]+=y;}
void add2(ll x,ll y){for(;x<=n;x+=x&-x)c2[x]+=y;}
int main()
{
scanf("%d%d",&n,&m);
rep(i,1,n)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
while(m--)
{
scanf("%s",s);
scanf("%lld%lld",&l,&r);
if (s[0]=='Q')
{
ll ans1=sum[r]-sum[l-1];
ll ans2=(r+1)*ask1(r)-l*ask1(l-1);
ll ans3=-ask2(r)+ask2(l-1);
WW(ans1+ans2+ans3);
}
else
{
scanf("%lld",&x);
add1(l,x);
add1(r+1,-x);
add2(l,l*x);
add2(r+1,-(r+1)*x);
}
}
}