Ultra-QuickSort+POJ-2299+OpenJ_Bailian-2299

题目链接

*题目大意:求逆序对的数目
题目数据:
n < 500,000,n表示最多有n组数据
0 ≤ a[i]999,999,999,a[i]表示每个数最大是99999999
限制:
Time limit:7000 ms
Memory limit:65536 kB
*题目分析:
*n的数据在可建树范围内,而a[i]的数据超出可建树范围
*所以数据需要离散化处理。
题目数据范围:
记录逆序对对数的变量需要由long long 类型的数据保存,例如5 4 3 2 1 ,逆序对数:4+3+2+1=10;是所给数据的2倍了。
离散化不再解释,那么我们如何去维护这样一棵树呢?
**例如 4 8 1 2 3这样一组数据。(题目中的数据不重复,这里不考虑去重)
解决思路:
第一步:将4 8 1 2 3同时存在 a[]数组 和 b[]数组 里。
第二步:对b[]数组按照从小到大的顺序排序:如下:1 2 3 4 8
第三步:询问a[]数组里的每个数在b[]数组里相对位置(第几大的数)
a[]   4 8 1 2 3
pos:  4 5 1 2 3
因为逆序对的数目和数的大小无关,而是数的相对大小。
第四步:4 5 1 2 3 的逆序对数
第五步:(核心的一步)
维护一棵:叶节点数为5的数,数的节点 表示在此区间有多少数。
(1)将4插入到第4个叶节点,查找此时(1~4)区间插入了几个数了,为0,用(1)减去0,就是前面比4大的数有几个,结果为02)将5插入到第5个叶节点,查找此时(1~5)区间插入了几个数了,为2,用(2)减去2,就是前面比5大的数有几个,结果为03)将1插入到第1个叶节点,查找此时(1~1)区间插入了几个数了,为1,用(3)减去1,就是前面比1大的数有几个,结果为24)将2插入到第2个叶节点,查找此时(1~2)区间插入了几个数了,为2,用(4)减去2,就是前面比2大的数有几个,结果为25)将3插入到第3个叶节点,查找此时(1~3)区间插入了几个数了,为3,用(5)减去3,就是前面比3大的数有几个,结果为2
总之,就是用当前  插入的数的总数量 - 插入的数里面不大于这个数的数的数目 == 这个数前面比这个数大的数的数目

<这是线段树的代码,在这个代码后面是树状数组的代码。>

//n < 500,000,0 ≤ a[i] ≤ 999,999,999, 
//逆序对+离散化处理 
//形成一个1~~n的空树
//m每次加入一个点a[i],就对区间里对应小区间和大区间加1
//然后统计这个点之前的区间里面有几个值,就是有几个小于它的值,然后用总的减去小于的,加上大于的。

//我们不回答[l,r]之间有几个数比h小,我们只需要回答比h小的数中有几个是介于[l,r]之间的
///
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int num=5000010;
int n,a[num],b[num],len;
struct section
{
	int L,R,sum;
}d[4*num];

void simplify(void)
{
	sort(b+1,b+n+1);
	for(int i=1;i<=n;i++)
	{
		int pos=lower_bound(b+1,b+n,a[i])-b;
		a[i]=pos;
	}
	return;
}

void update_tree(int k)
{
	d[k].sum = d[k<<1].sum + d[k<<1|1].sum; 
	return ;
}
void build_tree(int k,int L,int R)
{
	d[k].L=L;d[k].R=R;
	d[k].sum=0;
	if(L==R) return ;
	
	int mid=(L+R)>>1;
	build_tree(k<<1,L,mid);
	build_tree(k<<1|1,mid+1,R);
	
	update_tree(k);
	return ;	
}
//在空树中加入新点: 
void in_tree(int k,int L,int R)
{
	if(d[k].L==L&&d[k].R==R)
	{
		d[k].sum+=1;
		return ;
	}
	
	int mid=(d[k].L+d[k].R)>>1;
	if(L<=mid) in_tree(k<<1,L,R);
	if(R>mid) in_tree(k<<1|1,L,R);
	
	update_tree(k);
	return ;
}
//找到1~~~(x-1)里面有几个数啊 
int search_tree(int k ,int L,int R)
{	//L~~R;
	if(L <= d[k].L&&d[k].R <= R)
	{
		return d[k].sum;
	}
	  
	int mid=(d[k].L+d[k].R)>>1;
	int ans=0;
	
	if(L<=mid) ans+=search_tree(k<<1,L,R);
	if(R>mid)  ans+=search_tree(k<<1|1,L,R);
	
	return ans;			
}
int main()
{
	long long ans;
	while(scanf("%d",&n)&&n)
	{
		len=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			b[i]=a[i];
			if(a[i]>len) len=a[i];
		}
		
		simplify();
		
		build_tree(1,1,len);
		
		ans=0;
		for(int i=1;i<=n;i++)
		{
			in_tree(1,a[i],a[i]);
			ans+=(i-search_tree(1,1,a[i]));
		}
		
		printf("%lld\n",ans);
	}
	return 0;	
} 
//维护这样一个数组,记录前面几个数据了
//执行这样两种操作,1,插入数据
//2, 询问这段区间之前有几个比它小 
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int num=500010;
int c[num],a[num],b[num],n,len;
void int_i(void)
{
	for(int i=1;i<=len;i++)
	c[i]=0;
	return ;
}
void simplify(void)
{
	sort(b+1,b+n+1);
	len=unique(b+1,b+n+1)-(b+1);
	for(int i=1;i<=n;i++)
	{
		int pos=lower_bound(b+1,b+len+1,a[i]) - b;//这里是len,不是n了,注意 
		a[i]=pos;
	}
	return ;
}
int lowbit(int x)
{
	return x&(-x);
}
void update_point(int pos,int k)
{
	while(pos<=len)
	{
		c[pos]+=k;
		pos=pos+lowbit(pos);	
	}
}
int getsum(int pos)
{
	int ans=0;
	while(pos>0)
	{
		ans+=c[pos];
		pos-=lowbit(pos);
	}
	return ans;
}
int main()
{
	long long ans;
	while(scanf("%d",&n)&&n)
	{
	
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			b[i]=a[i];
		}
		simplify();
		int_i();
		ans=0;
		for(int i=1;i<=n;i++)
		{
			update_point(a[i],1);
			ans+=(i-getsum(a[i]));
		}
		
		printf("%lld\n",ans);
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值