线段树
和树状数组的功能类似 但是比树状数组强大的是
线段树可以进行区间的更新操作(不只是单点更新
如把数组1-3加5 或者把数组5-6全部改为4 等等
但是线段树的代码量比树状数组要大
记住线段树的内存是普通的四倍!!!
下面代码
![](https://img-blog.csdn.net/20170529165603198?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGxqZXJf/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
/*
线段树
输入一个数组
改变区间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=\n");
scanf("%d%d%d",&ll,&rr,&addd);
printf("input search l,r=\n");
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\n",k);
}
return 0;
}