• [笔记]: 线段树 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;
    }


  • 相关阅读:
    JQuery之在线引用
    SpringBoot之durid连接池配置
    VueJs之事件处理器
    VueJs之样式绑定
    VueJs之判断与循环监听
    PTA 7-8 暴力小学(二年级篇)-求出4个数字 (10分)
    PTA 7-7 交替字符倒三角形 (10分)
    PTA 7-5 阶乘和 (10分)
    PTA 7-4 哥德巴赫猜想 (10分)
    PTA 7-3 可逆素数 (15分)
  • 原文地址:https://www.cnblogs.com/xljxlj/p/7183640.html
Copyright © 2020-2023  润新知