线段树和树状数组的学习
树状数组
作用: 单点修改,区间查询
实现代码:
java
static class BIT{
int n ;
long[] tree;
BIT(int n){
this.n = n;
tree = new long[n+1];
}
int lowbit(int i){
return i & -i;
}
void add(int i, int v){
while( i <= n){
tree[i] += v;
i += lowbit(i);
}
}
long query(int i){
long ans = 0;
while( i > 0 ){
ans += tree[i];
i -= lowbit(i);
}
return ans;
}
}
应用
1) 求逆序对
使用树状数组维护每一个数出现的次数。当数组长度过长时,可以使用离散化进行优化。
java
//========不适用离散化 n: 数组长度 ==============
BIT b = new BIT(n);
int[] p = new int[n+1]; // 给定的数组
long ans = 0;
for(int i = 1;i <= n ; i++){
int smallerOrEqual = b.query(p[i]); // 查询小于或者等于p[i]的数字的个数
ans += (i-1) - smallerOrEqual; // 到此时,数组共有i个数 , p[i] 前面有 i-1 个数, i-1 - samllerOrEqual 是大于 p[i] 的个数
b.add(p[i],1); // 加入树状数组中
}
//==========使用离散化: 利用HashMap 实现 ===========
BIT b = new BIT(n);
//===== 离散化 ======
int[] p = new int[n];
int[] c =p.clone();
Arrays.sort(c); // 对数组进行排序
int idx = 1;
for(int x : c){
if(!map.containsKey(x)){
map.put(x,idx++);
}
}
//===== 使用树状数组统计逆序对=========
long ans = 0 ;
for(int i =0;i<n;i++){
int rank = map.get(p[i]); // 获取排序
int smallerOrEqual = b.query(rank); //看看排在rank前面有多个数
ans += (i-smallerOrEqual);
b.add(rank,1);
}
线段树
区间修改,区间查询
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
// 线段树
public class SegementTree {
static int MAXN = 100001;
// 线段树数组 最多4倍的节点数
static long[] sum = new long[MAXN<<2];
// 懒惰标记数组
static long[] add = new long[MAXN<<2];
//原始数组
static int[] arr = new int[MAXN];
static int n,m;
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer st;
public static void main(String[] args) {
n = nextInt();
m = nextInt();
for(int i =1 ;i<=n;i++){
arr[i] = nextInt();
}
build(1,n,1);
while(m-->0){
int op = nextInt();
if(op == 1) {
int x = nextInt();
int y = nextInt();
int k = nextInt();
add(x,y,k,1,n,1);
}else{
int x = nextInt();
int y = nextInt();
long ans = query(x,y,1,n,1);
System.out.println(ans);
}
}
}
public static void up(int i){
sum[i] = sum[i<<1] + sum[i<<1|1];
}
public static void lazy(int i, long v ,int len){
add[i] += v;
sum[i] += v*len;
}
public static void down(int i, int ln, int rn){
if(add[i]!=0){
lazy(i<<1,add[i],ln);
lazy(i<<1|1,add[i],rn);
add[i] = 0;
}
}
public static void add(int jobl, int jobr , long v ,int l ,int r , int i ){
if(jobl <= l && jobr >= r){
lazy(i,v ,r-l+1);
}else{
int mid = (l + r) >> 1;
down(i,mid-l+1,r-mid);
if(jobl <= mid) add(jobl,jobr ,v , l , mid ,i<<1);
if(jobr > mid) add(jobl,jobr,v,mid+1,r,i<<1|1);
up(i);
}
}
public static long query(int jobl, int jobr ,int l ,int r , int i ){
if(jobl <= l && jobr >= r){
return sum[i];
}else{
int mid = (l + r) >> 1;
long ans = 0;
down(i,mid-l+1,r-mid);
if(jobl <= mid) ans+=query(jobl,jobr , l , mid ,i<<1);
if(jobr>mid) ans+=query(jobl,jobr,mid+1,r,i<<1|1);
return ans;
}
}
public static void build(int l ,int r ,int i){
if(l == r) sum[i] = arr[l];
else{
int mid = (l+r) >> 1;
build(l,mid ,i<<1);
build(mid+1,r,i<<1|1);
up(i);
}
}
public static int nextInt(){
return Integer.parseInt(next());
}
public static String next(){
while (st == null || !st.hasMoreElements()) {
try {
st = new StringTokenizer(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
return st.nextToken();
}
}