• 【权值线段树】离散化介绍 (+利用 线段树 求逆序对)


    先介绍一下离散化
    桶排大家应该知道,就是开一个数组(下标为数值,记录了该数值的出现次数)然后遍历过去如果出现次数不为零,那就输出这些数字,理论时间复杂度可以达到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~

  • 相关阅读:
    BZOJ 2120 数颜色
    BZOJ 3289 Mato的文件管理
    BZOJ 2038 小Z的袜子
    BZOJ 1878 HH的项链
    洛谷P2709 小B的询问
    6491: Daydream
    问题 L: An Invisible Hand
    HDU-2177 取(2堆)石子游戏 (威佐夫博奕)
    (POJ-3279)Fliptile (dfs经典---也可以枚举)
    问题 J: Palindromic Password ( 2018组队训练赛第十五场) (简单模拟)
  • 原文地址:https://www.cnblogs.com/zhenglw/p/9507884.html
Copyright © 2020-2023  润新知