树状数组模板

树状数组

定义与引入:

树状数组处理的大多数问题,线段树都可以处理,但线段树可处理的问题,树状数组不一定可以.

解决问题类型:

单点修改,区间/单点查询;
好像没有太多可说的.

典型例题:

HH的项链
数星星

模板:

#include<bits/stdc++.h>
using namespace std;

const int N=2e5+20;
int num[N],c[N];//c->主角,树状数组,维护num的前缀和 
inline int lowbit(int x)
{
	return x&(-x);//此处是取得一个点的管理区间范围 ->x与-x按位与可得x的最大(2^n)因数 
}
int sum(int x)
{
	int s=0;
	while(x)
	{
		s+=c[x];//加上一个区间和 
		x-=lowbit(x);//减去一个区间的管理范围 
	}
	return s;
}
void update(int x,int st)//单点修改 
{
	while(x<=n)//n是输入进来的数据总范围 
	{
		c[x]+=st;
		x+=lowbit(x);
	} 
}

然后由于这个树状数组不能直接维护区间修改,所以有了一维差分树状数组。
它与原先的代码没有区别,只是在我们在某个区间加上一个东西时,去在左边界上加,右边界+1减即可。
但是这个时候它没有办法区间查询了。
怎么办呢,我们维护两个树状数组。
考虑下列式子(设ai为原值,di为i点差分):

i=1nai

=i=1nj=1idj

=j=1ndji=jn

=j=1ndj×(nj+1)

=j=1ndj×(n+1)j=1ndj×j

这俩式子分别维护即可。
这个式子转换还算显然,但是如果我们要求去做一个前缀和的前缀和去统计答案,我们就要复杂些了。
类似于上面,我们看我们要维护的信息,对信息进行转换。

i=1nj=1iaj

=i=1nj=1ik=1jdk

=k=1ndki=knj=ki

=k=1ndk×(nk+1)(nk+2)2

=k=1ndk×(n+1)(n+2)2k=1ndk×k×(2n+3)2+k=1ndk×k22

还是三个分别维护三个树状数组即可。
再往高阶前缀和也是一样的,所以k个树状数组可以维护(原式的)k-1阶前缀和。
例题链接

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define qr qr()
#define ps push_back
#define pa pair<int,int>
#define ve vector
#define fi first
#define se second
using namespace std;
const int N=5e5+200;
int n,m,num[N];ll c1[N*2],c2[N*2],c3[N*2];
int nn;
inline ll qr{
	ll x=0;char ch=getchar();bool f=0;
	while(ch>57||ch<48)f=(ch=='-')?1:0,ch=getchar();
	while(ch>=48&&ch<=57)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
inline void add(ll x,int pos){
	int tmp=pos;
	while(tmp<=nn){
		c1[tmp]+=x;
		c2[tmp]+=x*pos;
		c3[tmp]+=x*pos*pos;
		tmp+=tmp&-tmp;
	}
}
inline ll ask(int pos){
	ll ans=0;int tmp=pos;
	while(tmp){
		ans+=c1[tmp]*(pos+1)*(pos+2)-c2[tmp]*(pos*2+3)+c3[tmp];
		tmp-=tmp&-tmp;
	}return ans/2;
}
ve<int>app[N];
void init(){
	n=qr;qr;
	for(int i=1;i<=n;++i)
		num[i]=qr,app[num[i]].ps(i);
	nn=2*n+1;
	const int wy=n+1;
	ll ans=0;
	for(int i=0;i<n;++i){
		app[i].ps(n+1);
		int last=0;
		for(unsigned int j=0;j<app[i].size();++j){
			int y=2*j-last+wy,x=2*j-(app[i][j]-1)+wy;
			ans+=ask(y-1)-(x>=3?ask(x-2):0);
			add(1,x);
			add(-1,y+1);
			last=app[i][j];
		}last=0;
		for(unsigned int j=0;j<app[i].size();++j){
			int y=2*j-last+wy,x=2*j-(app[i][j]-1)+wy;
			add(-1,x);
			add(1,y+1);
			last=app[i][j];
		}
	}printf("%lld",ans);
}
int main(){
	// ios::sync_with_stdio(0);
	// cin.tie(0);
	// cout.tie(0);





	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);
	




	init();
	return 0;
}
posted @   SLS-wwppcc  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示