[笔记]: 线段树 2017-05-26 11:54 95人阅读 评论(0) 收藏

线段树
和树状数组的功能类似 但是比树状数组强大的是
线段树可以进行区间的更新操作(不只是单点更新
如把数组1-3加5 或者把数组5-6全部改为4 等等
但是线段树的代码量比树状数组要大
记住线段树的内存是普通的四倍!!!
下面代码
/*
线段树
输入一个数组
改变区间l到r 使a数组l到r每个加上d
求区间ll到rr的和 
样例:
10
1 2 3 4 5 6 7 8 9 10
5 7 2 
1 8 

输出
42 
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int w[1000];
struct node{
	int left;
	int right;
	int sum;
	int addv;
}a[1000];
void pushdown(int p){
	if(a[p].addv!=0) 
	{	//往下传addv的值 如果a[p].addv的值是0就不用继续往下传了
		a[2*p].addv+=a[p].addv;//因为是累加和 所以一定是+= 不是=
		a[2*p].sum+=a[p].addv*(a[2*p].right-a[2*p].left+1);//再更新子结点的sum值
		a[2*p+1].addv+=a[p].addv;//右结点 同上
		a[2*p+1].sum+=a[p].addv*(a[2*p+1].right-a[2*p+1].left+1);
		a[p].addv=0;//清空 
	}
}
void build(int l,int r,int p){//建树函数
	a[p].left=l;
	a[p].right=r;
	if(l==r) {a[p].sum=w[l];return;}
	if(l<r){
		build(l,(l+r)/2,2*p);
		build((l+r)/2+1,r,2*p+1);
		a[p].sum=a[2*p].sum+a[2*p+1].sum;
	}
}

int query(int l,int r,int p){//求区间和 函数  l,r是查询的区间
	if(a[p].left==l&&a[p].right==r) return a[p].sum;
	pushdown(p); 
	int mid=(a[p].left+a[p].right)/2;	//一定是比较r和mid 如果r<=mid 代表左  如果l>mid 代表右 
	if(r<=mid)	return query(l,r,2*p);	//只用往左子树找 递归调用的时候查询区间不变
	else if(l>mid) return query(l,r,2*p+1);		//只用往右子树找 递归调用的时候查询区间不变
	else return query(l,mid,2*p)+query(mid+1,r,2*p+1);	//两边都找

}
/*
更新区间 如果更新一个区间 那么从根到这个区间的值都要改变 此时的耗时太大 
所以增加一个addv的域 
每次要计算区间 就把sum的值加上 (r-l+1)*addv 如果已经计算过这个结点就不用往下算sum 
直接return就可以了 这样就可以减少计算次数
*/
void update(int l,int r,int p,int d){//更新区间函数
	if(a[p].left==l&&a[p].right==r){
		a[p].sum+=(r-l+1)*d;a[p].addv+=d;return;
	}
	int mid=(a[p].left+a[p].right)/2;
	pushdown(p);//传addv的值 
	if(r<=mid)	update(l,r,2*p,d);
	else if(l>mid) update(l,r,2*p+1,d);
	else {
		update(l,mid,2*p,d);
		update(mid+1,r,2*p+1,d);
	}
	a[p].sum=a[2*p].sum+a[2*p+1].sum;//算完子节点后再更新sum
}
int main(){
	int n;	
	int ll,rr,addd,x,y;
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&w[i]);
	build(1,n,1);//建树 
	
	printf("input change l,r,add=
");
	scanf("%d%d%d",&ll,&rr,&addd);
	printf("input search l,r=
");
	scanf("%d%d",&x,&y);
	
	update(ll,rr,1,addd);
	printf("answer = ");
	printf("%d",query(x,y,1));
	return 0;
}

线段树求逆序对

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 51000
#define MID(a,b) (a+b)>>1
#define R(a) (a<<1|1)
#define L(a) a<<1
typedef struct {
    int num,left,right;
}Node;
int ans[MAX];
Node Tree[MAX<<2];
int n;

void Build(int t,int l,int r)         //以1为根节点建立线段树
{
    int mid;
    Tree[t].left=l,Tree[t].right=r;
    if(Tree[t].left==Tree[t].right)
    {
        Tree[t].num=0;
        return ;
    }
    mid=MID(Tree[t].left,Tree[t].right);
    Build(L(t),l,mid);
    Build(R(t),mid+1,r);
}

void Insert(int t,int l,int r,int x)     //向以1为根节点的区间[l,r]插入数字1
{
    int mid;
    if(Tree[t].left==l&&Tree[t].right==r)
    {
        Tree[t].num+=x;
        return ;
    }
    mid=MID(Tree[t].left,Tree[t].right);
    if(l>mid)
    {
        Insert(R(t),l,r,x);
    }
    else if(r<=mid)
    {
        Insert(L(t),l,r,x);
    }
    else
    {
        Insert(L(t),l,mid,x);
        Insert(R(t),mid+1,r,x);
    }
    Tree[t].num=Tree[L(t)].num+Tree[R(t)].num;
}

int Query(int t,int l,int r)           //查询以1为根节点,区间[l,r]的和
{
    int mid;
    if(Tree[t].left==l&&Tree[t].right==r)
        return Tree[t].num;
    mid=MID(Tree[t].left,Tree[t].right);
    if(l>mid)
    {
        return Query(R(t),l,r);
    }
    else if(r<=mid)
    {
        return Query(L(t),l,r);
    }
    else
    {
        return Query(L(t),l,mid)+Query(R(t),mid+1,r);
    }
}


int main()
{
    int a,n,i,t;
    scanf("%d",&t);
    long long int k;
    while(t--)
    {
        scanf("%d",&n);
        memset(Tree,0,sizeof(Tree));  //初始化线段树
        Build(1,1,n);
        for(i=1;i<=n;i++)             //输入n个数
        {
            scanf("%d",&ans[i]);
        }
        for(i=1,k=0;i<=n;i++)
        {
            a=ans[i];
            Insert(1,a,a,1);          //把线段树[ans[i],ans[i]]区间的值插入为1
            k=k+(i-Query(1,1,a));     //查询区间[1,ans[i]]值的总和,逆序数等于i-[1,ans[i]]
        }
        printf("%lld
",k);
    }
    return 0;
}


原文地址:https://www.cnblogs.com/xljxlj/p/7183640.html