算法1
树状数组
-
1、
lowbit(x)
:返回x
的最后一位1
-
2、
add(x,v)
:在x
位置加上v
,并将后面相关联的位置也加上v
-
3、
query(x)
:询问x
的前缀和
时间复杂度 $O(logn)$
参考文献
蓝桥杯辅导课
Java 代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
static int N = 100010;
static int n;
static int m;
static int[] a = new int[N];
static int[] tr = new int[N];
public static int lowbit(int x)
{
return x & -x;
}
//在x位置加上v,并将后面相关联的位置也加上v
public static void add(int x,int v)
{
for(int i = x;i <= n;i += lowbit(i)) tr[i] += v;
}
//前缀和
public static int query(int x)
{
int res = 0;
for(int i = x;i >= 1;i -= lowbit(i)) res += tr[i];
return res;
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] s1 = reader.readLine().split(" ");
n = Integer.parseInt(s1[0]);
m = Integer.parseInt(s1[1]);
String[] s2 = reader.readLine().split(" ");
for(int i = 1;i <= n;i++) a[i] = Integer.parseInt(s2[i - 1]);
//搭建树状数组
for(int i = 1;i <= n;i++) add(i,a[i]);
while(m -- > 0)
{
String[] s3 = reader.readLine().split(" ");
int k = Integer.parseInt(s3[0]);
int x = Integer.parseInt(s3[1]);
int y = Integer.parseInt(s3[2]);
//k = 0 是询问[x,y]的区间和,k = 1是在x位置添加y元素
if(k == 0) System.out.println(query(y) - query(x - 1));
else add(x,y);
}
}
}
算法2
线段树
-
1、
pushup(u)
:用子节点信息来更新当前节点信息(把信息往上传递) -
2、
build(u,l,r)
:在一段区间上初始化线段树,其中u
表示根结点,l
表示左边界,r
表示右边界 -
3、
query(u,l,r)
:查询某段区间的和,其中u
表示根结点,l
表示左边界,r
表示右边界 -
4、
modify(u,x,v)
:修改操作,在u
结点中,x
位置加上v
修改操作
查询操作
时间复杂度 $O(logn)$
参考文献
蓝桥杯辅导课
Java 代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
static int N = 100010;
static int n;
static int m;
static int[] w = new int[N];
static Node[] tr = new Node[N * 4];
//用子节点信息来更新当前节点信息(把信息往上传递)
public static void pushUp(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
//在一段区间上初始化线段树,其中u表示根结点,l表示左边界,r表示右边界
public static void build(int u,int l,int r)
{
if(l == r) tr[u] = new Node(l,r,w[r]);
else
{
tr[u] = new Node(l,r,0);
int mid = l + r >> 1;
build(u << 1,l,mid);
build(u << 1 | 1,mid + 1,r);
pushUp(u);
}
}
//查询某段区间的和,其中u表示根结点,l表示左边界,r表示右边界
public static int query(int u,int l,int r)
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
if(l <= mid) sum = query(u << 1,l,r);
if(r > mid) sum += query(u << 1 | 1,l,r);
return sum;
}
//修改操作,在u结点中,x位置加上v
public static void modify(int u,int x,int v)
{
if(tr[u].l == tr[u].r) tr[u].sum += v;
else
{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u << 1,x,v);
else modify(u << 1 | 1,x,v);
pushUp(u);
}
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] s1 = reader.readLine().split(" ");
n = Integer.parseInt(s1[0]);
m = Integer.parseInt(s1[1]);
String[] s2 = reader.readLine().split(" ");
for(int i = 1;i <= n;i++) w[i] = Integer.parseInt(s2[i - 1]);
//搭建线段树
build(1,1,n);
while(m -- > 0)
{
String[] s3 = reader.readLine().split(" ");
int k = Integer.parseInt(s3[0]);
int x = Integer.parseInt(s3[1]);
int y = Integer.parseInt(s3[2]);
//k = 0 是询问[x,y]的区间和,k = 1是在x位置添加y元素
if(k == 0) System.out.println(query(1,x,y));
else modify(1,x,y);
}
}
}
//段结点
class Node
{
public int l;//左边界
public int r;//右边界
public int sum;//当前块的总和
public Node(int l,int r,int sum)
{
this.l = l;
this.r = r;
this.sum = sum;
}
}
这里一共4*N个节点,怎么算的
写的真好!!!
奶奶滴,怎么写的这么好
太棒了,大佬!
真不戳
太棒了吧,结构清晰,赞赞赞!!!!!!!!!!!!!!!!!!!
请问数组哪里为什么要开4倍呢
最后一层有n个点,倒数第二层是n / 2,依次类推:n + n / 2 + n / 4 + …… 最多小于4n,为了防止爆掉,所以要开4倍!
%%%
太赞了,非常方便!!!比看视频省时多了,感谢。辛苦了
还是得看视频理解深刻hh,文章适合复习hh
请问在哪个课的视频里有啊谢谢啦
蓝桥杯,提高课
提高课程哪里有树状数组啊,我点开那个标签发现里面直接用了
修改:树状数组中
C[2] = A[2] + C[1] = A[1] + A[2]
第一个树状数组有点小错误,应该是lowbit(i) 不是lowbit(x)
谢谢提醒,已改正
这道题目不是板子题吗?
是滴
你应该写zkw线段树卡常,然后你就rank1了
膜大佬
大聚聚怎么能说这样的话,怎么能因为ioi差0.1分ak就自暴自弃呢,ccf不是都让你终身国集了吗,你还可以明年再战呀