火柴排队 归并 or 树状数组

http://codevs.cn/problem/3286/

题面在 ↑ 面呢。

题意:让你求两个数列的最小差的平方和最少需要交换几次(只能周围交换)。刚开始听人说是求逆序对,我学了个归并就来搞这个题了。想了半天也没想明白怎么求逆序对啊。然后找了找题解。首先想什么时候保证两个数列差最小,一定是第一个数列第一大的对应第二个数列第一大的...依次类推。这样就可以再开两个数组记录一下他们的编号,然后利用sort结构体按照数的大小从小到大排序,最后开一个数组记录第一个数列中第一大的序号对应在第二个数列中第一大的序号是什么位置既 zh [ xh1 [ i ] ] = xh2 [ i ]  。然后求这个数组的逆序对,然后对答案去%输出

归并排序求逆序对的思想:

二分出两个数组来,保证两个数组都是递增有序的,然后从两个数组中分别取出第一个数字比较,如果第二个数小于第一个数则ans+=第一个数组中剩下的数。正确性自行证明其实显然。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxx=100007;
struct ha
{
	LL x,xh;
}y1[maxx];
ha y2[maxx];
LL zh[maxx],t[maxx];
LL n,ans=0;
void _marge(LL l,LL r)
{
	
	if(l==r) return ;
	LL mid=(l+r)/2;
	_marge(l,mid);
	_marge(mid+1,r);
	LL pl=l,pr=mid+1,p=l;
	while (pl<=mid || pr<=r)
	{
		if(pl>mid)
		{
			t[p]=zh[pr];
			p++;
			pr++;
		}
		else if(pr>r)
		{
			t[p]=zh[pl];
			p++;
			pl++;
		}
		else if(zh[pr]<zh[pl])
		{
			ans+=mid-pl+1;
			t[p]=zh[pr];
			p++;
			pr++;
		}
		else 
		{
			t[p]=zh[pl];
			p++;
			pl++;
		}
	}
	for(int i=l;i<=r;i++)
		zh[i]=t[i];
	return ;
}
LL cp(ha a,ha b)
{return a.x<b.x;}
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&y1[i].x);
		y1[i].xh=i;	
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&y2[i].x);
		y2[i].xh=i;	
	}
	sort(y1+1,y1+1+n,cp);
	sort(y2+1,y2+1+n,cp);
	for(int i=1;i<=n;i++)
		zh[y1[i].xh]=y2[i].xh;
	_marge(1,n);
	printf("%lld",ans%99999997);
	return 0;
}

树状数组求逆序对思想:(感谢lc的help)

给每个数组一个序号,然后sort结构体排序。然后按照序号放数。每次放之前用树状数组求一下当前序号的前缀和。ans+=每次前缀和。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxx=100007;
struct ha
{
	LL x,xh;
}y1[maxx];
ha y2[maxx];
ha zh[maxx];
LL n,ans=0;

LL c[1000000];
void change(LL x,LL d)
{
	while(x<=n)
	{
		c[x]+=d;
		x+=x&(-x);
	}
}
LL ask(LL x)
{
	LL ans=0;
	while(x>0)
	{
		ans+=c[x];
		x-=x&(-x);
	}
	return ans;
}
LL cp(ha a,ha b)
{return a.x<b.x;}
LL cp2(ha a,ha b)
{return a.x>b.x;}
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&y1[i].x);
		y1[i].xh=i;	
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&y2[i].x);
		y2[i].xh=i;	
	}
	sort(y1+1,y1+1+n,cp);
	sort(y2+1,y2+1+n,cp);
	for(int i=1;i<=n;i++)
		zh[y1[i].xh].x=y2[i].xh;
	for(int i=1;i<=n;i++)
		zh[i].xh=i;
	sort(zh+1,zh+1+n,cp2);
	for(int i=1;i<=n;i++)
	{
		ans+=ask(zh[i].xh-1);
		change(zh[i].xh,1);
	}
	printf("%lld",ans%99999997);
	return 0;
}

 

转载于:https://my.oschina.net/u/3009052/blog/785288

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值