zoukankan      html  css  js  c++  java
  • 【权值线段树】离散化介绍 (+利用 线段树 求逆序对)

    先介绍一下离散化
    桶排大家应该知道,就是开一个数组(下标为数值,记录了该数值的出现次数)然后遍历过去如果出现次数不为零,那就输出这些数字,理论时间复杂度可以达到O(N)但是由于内存限制,不能开很大的数组。

    然而 如果某个数列中的数字不要求大小确定,只要求这些数字有相对的大小就够了的话,离散化就有了用武之地

    举个例子:数列

    3 8 7 5 2000000000000000
    

    我们发现有几个数之间差距很大,但是我们用不到数值的大小,只要求相对大小,那怎么办呢?
    观察下面的数列:

    1 4 3 2 5
    

    真巧,这个数列和上面的数列各个数字之间的相对大小是一样的并且,让很大的数据缩小了,这样离散化了之后就可以处理一些原本处理不了的问题

    离散化比较OK的复杂度是O(NlogN)
    STL大法好,代码见下:

    	for(int i=1;i<=n;++i)
    		a[i]=read(),b[i]=a[i];//读入数组a,b数组记录下,等会儿用(read是读入优化)
    	sort(b+1,b+n+1);//排序b数组,避免a数组顺序打乱
    	int len=unique(b+1,b+n+1)-b-1;//unique:STL自带函数,去重并返回去重后数组的长度+1(所以这里后面要-1)
    	//(原理上来讲不是这样,但这样理解方便)
    	for(int i=1;i<=n;++i){
    		int pos=lower_bound(b+1,b+n+1,a[i])-b;//STL自带函数,返回b(有序数组)中第一个大于等于a[i]的位置
    		a[i]=pos;//改变这个值为找到的位置
    	} 
    
    

    介绍一种神奇的算法:

    权值线段树

    顾名思义,该线段树中存储的并不是普通线段树记录的线段端点,而是 类似一个桶一样的东西

    最简单的例子:求逆序对
    该题可以用树状数组做,可以用归并排序思想做,可以用splay做......
    不过,既然可以用树状数组用,也可以用线段树做

    【思路】

    先建一棵很大的线段树,然后用权值为下标建树,每一次读到一个数,就找比这个数大的个数,找到了之后加到ans里,接着更新线段树(这个数出现的次数+1)
    PS:这题要加离散化,否则……你懂的

    #include<iostream>
    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #define lson i << 1
    #define rson i << 1 | 1
    #define M 40005
    using namespace std;
    
    inline int read(){
    	char chr=getchar();
    	int f=1,ans=0;
    	while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();}
    	while(isdigit(chr))  {ans=ans*10;ans+=chr-'0';chr=getchar();}
    	return ans*f;
    
    }
    int n;
    struct Node{
    	int l,r,v;
    }t[M<<2];
    int a[M],b[M];
    void kai(){
    	freopen("test1.txt","r",stdin);
    }
    
    void push_up(int i){
    	t[i].v=t[lson].v+t[rson].v;
    }//向上更新
    
    void build(int i,int l,int r){//建树
    	t[i].l=l;	t[i].r=r;	t[i].v=0;
    	if(l==r){
    		return;
    	}
    	int mid=t[i].l+t[i].r>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    }
    
    void updata(int i,int x){
    	if(t[i].l==t[i].r){
    		++t[i].v;//这个数字出现的次数+1
    		return;
    	}
    	int mid=t[i].l+t[i].r>>1;
    	if(x<=mid) updata(lson,x);
    	else       updata(rson,x);
    	push_up(i);
    }//更新节点
    
    int query(int i,int l,int r){
    	if(l<=t[i].l&&t[i].r<=r) return t[i].v;
    	int mid=t[i].l+t[i].r>>1;
    	int x=0;
    	if(l<=mid) x+=query(lson,l,r);
    	if(mid<r)  x+=query(rson,l,r);
    	return x;
    }//询问
    
    int main(){
    //	kai();
    	n=read();
    	int ans=0;
    	build(1,1,M);
    	for(int i=1;i<=n;++i)
    		a[i]=read(),b[i]=a[i];
    	sort(b+1,b+n+1);
    	int len=unique(b+1,b+n+1)-b-1;
    	for(int i=1;i<=n;++i){
    		int pos=lower_bound(b+1,b+n+1,a[i])-b;
    		a[i]=pos;
    	} //离散化
    	for(int i=1;i<=n;++i){
    		int x=a[i];
    		ans+=query(1,x+1,M);//找比这个数大的数的出现的总次数
    		updata(1,x);//这个数出现次数+1
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    打完收工

    hiahiahia~

  • 相关阅读:
    vue中的 computed 和 watch 的区别
    mysql8.0 初始化数据库及表名大小写问题
    sql server alwayson 调整数据文件路径
    zabbix 自定义监控 SQL Server
    mysql 创建用户及授权
    mysql 设置从库只读模式
    mysql8.0 主从复制安装及配置
    centos8.0安装mysql8.0
    centos8替换阿里数据源
    npm publish 报错 【you or one of your dependencies are requesting a package version that is forbidden by your security policy】
  • 原文地址:https://www.cnblogs.com/zhenglw/p/9507884.html
Copyright © 2011-2022 走看看