首页 > 其他分享 >树状数组

树状数组

时间:2024-07-31 15:06:54浏览次数:7  
标签:const 树状 int lowbit namespace 数组 include dp

树状数组

一、单点修改和区间查询

lowbit函数

\[lowbit(x)=x\&(-x) \]

作用:得到 \(x\) 二进制最右侧的1。

如,\(x=(0010010011000)_2\) ,则 \(-x=x取反+1=(1101101101000)_2\) , \(x\&(-x)=(0000000001000)_2\) 。

原理

用 \(c[i]\) 表示树状数组,\(a[i]\) 表示原数组。

将 \(c[i]\) 中下标转为二进制后,定义 \(c[i]=a[i-lowbit(i)+1]+a[i-lowbit(i)+2]+...+a[i]\) 。

对于查询 \([L,R]\) ,利用前缀和想法将其拆为 \(\sum_{i=1}^{R}a_i-\sum_{i=1}^{L-1}a_i\) ,考虑求解 \(\sum_{i=1}^xa_i\) 。将 \(x\) 转为二进制,让 \(ans+=c[x]\) 即加上 \([x-lowbit(x)+1,x]\) 的值,并让 \(x-=lowbit(x)\) 。不断重复上述操作即可得到答案。

对于单点 \(x\) 位置的修改,发现x处的改动会影响 \(c[x+lowbit(x)]\) 的值,于是不断让 \(x+=lowbit(x)\) 修改 \(c[x]\) 处的值即可。

P3374

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int c[N],a,n,m;
int lowbit(int x){
	return x&(-x);
}
void add(int x,int v){
	for(int i=x;i<=n;i+=lowbit(i)){
		c[i]+=v;
	}
}
int query(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i)){
		ans+=c[i];
	}
	return ans;
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&a);
		add(i,a);
	}
	int opt,x,y;
	while(m--){
		scanf("%d %d %d",&opt,&x,&y);
		if(opt==1) add(x,y);
		else printf("%d\n",query(y)-query(x-1));
	}
	return 0;
}

二、区间修改和单点查询

利用差分与前缀和思想,考虑用树状数组维护差分数组 \(s[i]\) 。

对于修改 \([L,R]\) ,只需变为单点修改 \(s[L]\) 和 \(s[R+1]\) 即可。

P3368

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,m,a[N],c[N];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int v){
	for(int i=x;i<=n;i+=lowbit(i)){
		c[i]+=v;
	}
}
int query(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i)){
		ans+=c[i];
	}
	return ans;
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
	}
	int opt,x,y,k;
	while(m--){
		scanf("%d %d",&opt,&x);
		if(opt==1){
			scanf("%d %d",&y,&k);
			add(x,k);
			add(y+1,-k);
		}
		else{
			printf("%d\n",a[x]+query(x));
		}
	}
	return 0;
}

一些题目

P1908

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=5e5+5;
int n,a[N],c[N],s[N];
LL ans;
int lowbit(int x){
	return x&(-x);
}
void add(int x){
	for(int i=x;i<=n;i+=lowbit(i)){
		c[i]++;
	}
}
int query(int x){
	int cnt=0;
	for(int i=x;i;i-=lowbit(i)){
		cnt+=c[i];
	}
	return cnt;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		s[i]=a[i];
	}
	sort(s+1,s+1+n);
	for(int i=1;i<=n;++i){
		a[i]=lower_bound(s+1,s+n+1,a[i])-s;
		ans+=(LL)i-1ll-(LL)query(a[i]);
		add(a[i]);
	}
	printf("%lld",ans);
	return 0;
}

P2982

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+5;
int n,hd[N],tot,dfn[N],d[N],p[N],idx;
LL ans,c[N];
struct node{
	int nxt,to;
}edge[N<<1];
void add_edge(int x,int y){
	edge[++tot]=node{hd[x],y};
	hd[x]=tot;
}
void dfs(int x,int fa){
	dfn[x]=++idx;
	for(int i=hd[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		if(y==fa) continue;
		dfs(y,x);
	}
	d[x]=idx;
}
int lowbit(int x){
	return x&(-x);
}
void add(int x,LL v){
	for(int i=x;i<=n;i+=lowbit(i)){
		c[i]+=v;
	}
}
LL query(int x){
	LL cnt=0;
	for(int i=x;i;i-=lowbit(i)){
		cnt+=c[i];
	}
	return cnt;
}
int main(){
	scanf("%d",&n);
	int x,y;
	for(int i=1;i<n;++i){
		scanf("%d %d",&x,&y);
		add_edge(x,y);
		add_edge(y,x);
	}
	dfs(1,1);
	for(int i=1;i<=n;++i){
		scanf("%d",&p[i]);
	}
	for(int i=1;i<=n;++i){
		printf("%lld\n",query(dfn[p[i]]));
		add(dfn[p[i]],1);
		add(d[p[i]]+1,-1);
	}
	return 0;
}

P2344

#include <bits/stdc++.h>
#define mod 1000000009
using namespace std;
const int N=1e5+5;
int n,a[N],sum[N],dp[N],ans,c[N],tmp[N];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int v){
	for(int i=x;i<=n+1;i+=lowbit(i)){
		c[i]=(c[i]+v)%mod;
	}
}
int query(int x){
	int res=0;
	for(int i=x;i;i-=lowbit(i)){
		res=(res+c[i])%mod;
	}
	return res;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		tmp[i]=sum[i]=sum[i-1]+a[i];
	}
	sort(tmp,tmp+1+n);
	for(int i=0;i<=n;++i){
		sum[i]=lower_bound(tmp,tmp+n+1,sum[i])-tmp+1;
	}
	add(sum[0],1);
	for(int i=1;i<=n;++i){
		ans=query(sum[i]);
		add(sum[i],ans);
	}
	printf("%d",ans%mod);
	// dp[0]=1;
	// for(int i=1;i<=n;++i){
	// 	for(int j=0;j<i;++j){
	// 		if(sum[i]-sum[j]>=0){
	// 			dp[i]=(dp[i]+dp[j])%mod;
	// 		}
	// 	}
	// }
	// printf("%d",dp[n]);
	return 0;
}

标签:const,树状,int,lowbit,namespace,数组,include,dp
From: https://www.cnblogs.com/mj666/p/18334686

相关文章

  • 如何根据数组中的数据在 matplotlib 中创建 3D 线图?
    我已经使用SciPy和脚本对洛伦兹方程进行了数值求解:#LorenzEquationsSciPysolverimportnumpyasnpfromscipyimportintegratefrommathimportcosfrommatplotlibimportpyplotasplta,b=0,100sigma,rho,beta=10,28,8/3N=1000000h=(b-a)/f......
  • 后缀数组 - half
    后缀数组后缀数组可以解决有关后缀的问题废话。那么暴力做法肯定是把每个后缀全部取出来,然后按照字典序排序,但是这样复杂度是\(\Theta(n^2\logn)\)的。后缀数组可以解决以下问题:最长重复子串多个串的最长公共子串不同子串个数算法详解面对这些问题,我们需要\(3\)个数......
  • C语言——数组和排序
    C语言——数组和排序数组数组的概念数组的初始化数组的特点排序选择排序冒泡排序插入排序二分查找数组数组的概念数组是一组数据;数组是一组相同类型的数据或变量的集合;应用场景:用于批量的处理多个数据;语法:类型说明符数组名[常量表达式]类型说明符也就......
  • 数组及数组JVM内存划分day4
    java中第一个存储数据的容器:数组特点:1、数组的长度大小是固定的2、同一个数组中,存储的元素数据类型是一样的数组的定义语句格式:数据类型[]数组名;举例:int[]arr;//定义了一个可以存储int类型的一维数组,数组名叫做arr......
  • 数组元素逆序
    /*数组元素逆序(就是把元素对调)涉及数组元素交换的逻辑的时候,可以定义一个中间变量,作用是临时将值存储一下*/publicclassArrayTest3{publicstaticvoidmain(String[]args){int[]arr={1,2,3,4,5,6,7};System.out.println("......
  • 找出数组中最大和最小值
    找出数组中最大和最小值/*数组获取最值(获取数组中的最大值最小值)*/publicclassArrayTest2{publicstaticvoidmain(String[]args){int[]arr={123,451,45,12,556,12,412};//1、默认将第一个元素作为最大值以及最小值int......
  • (nice!!!)LeetCode 2952. 需要添加的硬币的最小数量(贪心、数组)
    题目:2952.需要添加的硬币的最小数量思路:假设区间[1,s-1]的数都可组合得到,当遍历到x=coins[i]时,1、当x<=s时,可以组合的数就是区间[1,s-1]和区间[x,s-1+x]的交集,即区间[1,s-1+x]2、当x>s时,区间[1,s-1]和区间[x,s-1+x]没有交集,那我就只能通过添加一个数来实现了。在这里......
  • 一维数组
    创建数组一维数组的创建和初始化:声明数组:javaint[]myIntArray;//声明一个整数类型的数组分配内存空间(初始化数组):javamyIntArray=newint[5];//分配一个可以存储5个整数的数组分配数组元素:javamyIntArray[0]=10;myIntArray[1]=20;myIntArray[2]=3......
  • 数组概念
    数组数组(Array)是一种基本的数据结构,用于存储固定数量的元素,这些元素通常是相同类型的。数组提供了一种方式来访问和操作集合数据。以下是数组的一些基本概念:固定大小:一旦声明,数组的大小就不能改变。例如,如果你声明一个包含10个整数的数组,你就不能将其扩展到10个以上的元素。......
  • 数组的算法
    冒泡法冒泡排序(BubbleSort)是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的......