zoukankan      html  css  js  c++  java
  • POJ 2299-Ultra-QuickSort-线段树的两种建树方式

    此题有两种建树方式!

    Description

    In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order.

    For the input sequence
    9 1 0 5 4 ,
    Ultra-QuickSort produces the output
    0 1 4 5 9 .

    Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.

    Input

    The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.

    Output

    For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.

    Sample Input

    5
    9
    1
    0
    5
    4
    3
    1
    2
    3
    0
    

    Sample Output

    6
    0
    

    题目大意:

    给定一个排列,求这个排列的逆序数

    逆序的定义

    在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。

    核心思想:

    我们用结构体来存储排列:
    在这里插入图片描述
    a[i].x表示第i个数的数值,a[i].id表示第i个数的位置。

    对于每一个数a[i].x,我们找到位置在它前面并且数值比它大的数的个数c[i],c数组的和就是答案。
    线段树
    我们在统计个数的时候,数要满足两个条件:
    1、位置在它前面
    2、数值比它大

    我们在空间时间上分别加以控制来满足这两个条件。
    空间:线段树的左右区间
    时间:线段树的更新顺序
    所以此题有两种建树方式

    方式一

    空间上加以控制来满足位置在它前面的条件;
    时间上加以控制来满足数值比它大的条件。

    线段树的左右区间表示原排列中数的位置,通过控制查询时左右区间的端点来满足位置在它前面的条件。
    我们将a数组按x降序排列,按照新的顺序依次更新线段树,数值的大的先进入(更新)线段树,所以现在从线段树上查询到的个数一定满足数值比它大的条件。

    此方式建树不需要离散化,下面的方式需要离散化。

    方式二

    空间上加以控制来满足数值比它大的条件;
    时间上加以控制来满足位置在它前面的条件。
    线段树的左右区间表示原排列中数的数值,通过控制查询时左右区间的端点来满足数值比它大的条件。
    我们将a数组按id升序排列(也就是原顺序),位置靠前的先进入(更新)线段树,所以现在从线段树上查询到的个数一定满足位置在它前面的条件。

    注意:此题目数的数值范围是1e10,将数值作为线段树区间长度开不出这么大的数组。解决方法是离散化
    在这里插入图片描述
    离散前后的映射函数是:
    在这里插入图片描述

    离散后的数值范围是不同数值的个数,也就是5e5。这样就可以开数组建树了。

    方式一代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=5e5+20;
    char s[20];
    //存原排列 
    struct node{
    	int x,id;
    }a[N];
    struct tnode{
    	int l,r,sum;
    }tr[N<<2];
    void pushup(int m)
    {
    	tr[m].sum=tr[m<<1].sum+tr[m<<1|1].sum;
    	return;
    }
    void build(int m,int l,int r)
    {
    	tr[m].l=l;
    	tr[m].r=r;
    	if(l==r)
    	{
    		tr[m].sum=0;
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(m<<1,l,mid);
    	build(m<<1|1,mid+1,r);
    	pushup(m);
    	return;
    }
    void update(int m,int x)
    {
    	if(tr[m].l==x&&tr[m].r==x)
    	{
    		tr[m].sum=1;
    		return;
    	}
    	int mid=(tr[m].l+tr[m].r)>>1;
    	if(x<=mid)
    		update(m<<1,x);
    	else
    		update(m<<1|1,x);
    	pushup(m);
    	return;
    }
    int query(int m,int l,int r)
    {
    	if(tr[m].l==l&&tr[m].r==r)
    		return tr[m].sum;
    	int mid=(tr[m].l+tr[m].r)>>1;
    	if(r<=mid)
    		return query(m<<1,l,r);
    	if(l>mid)
    		return query(m<<1|1,l,r);
    	return query(m<<1,l,mid)+query(m<<1|1,mid+1,r);
    }
    bool cmp(node p,node q)
    {
    	return p.x>q.x;
    }
    int main()
    {
    	int n;
    	while(1)
    	{
    		scanf("%d",&n);
    		if(!n)
    			break;
    		//根据n直接建树 
    		build(1,1,n);
    		//输入
    		for(int i=1;i<=n;i++)
    		{
    			scanf("%d",&a[i].x);
    			a[i].id=i;
    		}
    		//排序,x大的先进入线段树 
    		sort(a+1,a+n+1,cmp);
    		//边更新边查询 
    		ll ans=0;
    		for(int i=1;i<=n;i++)
    		{
    			ans+=query(1,1,a[i].id);
    			update(1,a[i].id);
    		}
    		//输出 
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    方式二代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=5e5+20;
    int b[N],cnt;
    //存原排列 
    struct node{
    	int x,id;
    }a[N];
    struct tnode{
    	int l,r,sum;
    }tr[N<<2];
    void pushup(int m)
    {
    	tr[m].sum=tr[m<<1].sum+tr[m<<1|1].sum;
    	return;
    }
    void build(int m,int l,int r)
    {
    	tr[m].l=l;
    	tr[m].r=r;
    	if(l==r)
    	{
    		tr[m].sum=0;
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(m<<1,l,mid);
    	build(m<<1|1,mid+1,r);
    	pushup(m);
    	return;
    }
    void update(int m,int x)
    {
    	if(tr[m].l==x&&tr[m].r==x)
    	{
    		tr[m].sum=1;
    		return;
    	}
    	int mid=(tr[m].l+tr[m].r)>>1;
    	if(x<=mid)
    		update(m<<1,x);
    	else
    		update(m<<1|1,x);
    	pushup(m);
    	return;
    }
    int query(int m,int l,int r)
    {
    	if(tr[m].l==l&&tr[m].r==r)
    		return tr[m].sum;
    	int mid=(tr[m].l+tr[m].r)>>1;
    	if(r<=mid)
    		return query(m<<1,l,r);
    	if(l>mid)
    		return query(m<<1|1,l,r);
    	return query(m<<1,l,mid)+query(m<<1|1,mid+1,r);
    }
    bool cmp1(node p,node q)
    {
    	return p.x<q.x;
    }
    bool cmp2(node p,node q)
    {
    	return p.id<q.id;
    }
    int getid(int x)
    {
    	return lower_bound(b,b+cnt,x)-b+1;
    }
    int main()
    {
    	int n;
    	while(1)
    	{
    		scanf("%d",&n);
    		if(!n)
    			break;
    		//输入 
    		a[0].x=-1;
    		for(int i=1;i<=n;i++)
    		{
    			scanf("%d",&a[i].x);
    			a[i].id=i;
    		}
    		//先离散化 
    		sort(a+1,a+n+1,cmp1);
    		cnt=0;
    		for(int i=1;i<=n;i++)
    			if(a[i].x!=a[i-1].x)
    				b[cnt++]=a[i].x;
    		//根据离散后的cnt建树 
    		build(1,1,cnt);
    		//别忘了把a数组sort回来 
    		sort(a+1,a+n+1,cmp2);
    		//边更新边查询 
    		ll ans=0;
    		for(int i=1;i<=n;i++)
    		{
    			int t=getid(a[i].x);
    			ans+=query(1,t,n);
    			update(1,t);
    		}
    		//输出 
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    JS分页条插件
    C#Lambda
    常用CSS样式速查
    简易表格编辑器
    使用template
    js 时间类函数
    数据库表增删查改帮助类
    使用github
    box-shadow属性
    box-sizing属性
  • 原文地址:https://www.cnblogs.com/NothingbutFight/p/11306760.html
Copyright © 2011-2022 走看看