线段树
和树状数组的功能类似 但是比树状数组强大的是
线段树可以进行区间的更新操作(不只是单点更新
如把数组1-3加5 或者把数组5-6全部改为4 等等
但是线段树的代码量比树状数组要大
记住线段树的内存是普通的四倍!!!
下面代码
![](//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; }