P1966 【NOIP2013】 火柴排队

#$Description$ [题面](https://www.luogu.org/problem/P1966) 有两列火柴,每个火柴有一个高度,定义两列火柴之间的距离为$\sum\limits_{i=1}^n (a_i-b_i)^2$,其中$a_i$表示第一列火柴中第$i$个火柴的高度,$b_i$表示第二列火柴中第$i$个火柴的高度 可以交换一列中任意两根相邻火柴的位置,使得最终两列火柴距离最小,求最少交换次数 #$Solution$ 想让两列火柴距离最小,显然要使第一列中第$i$高的火柴对应第二列中第$i$高的火柴,证明略。

分别开两个数组\(c,d\)\(c[i]\)表示在\(a\)数组中第\(i\)大的数所在的位置(如果出现相同的数按照下标排序)

比如下面这个例子,理解一下

c 1 4 5 2 3
a 1 3 5 1 2

\(d[i]\)同理表示\(b\)数组

这实际是一种离散化,不过我们常用的离散化\(c[i]\)表示\(i\)的排名,这个离散化\(c[i]\)表示排名为\(i\)所在的下标

然后令\(p[c[i]]=d[i]\),表示他们之间的对应关系

比如下面这样

c 1 4 5 2 3
d 3 2 1 5 4
p 3 5 4 2 1

最终要求在各自数组中排名相同的在同一位置,即\(p[i]=i\)

所以要把\(p\)数组变成一个升序数列,假如\(p[5]=3,p[3]=5\)表示表示排名为\(i\)的元素在\(a\)中是第\(5\)个,在\(b\)中是第\(3\)个,排名为\(j\)的元素在\(a\)中是第\(3\)个,在\(b\)中是第\(5\)

结论:使\(p\)数组由无序变为升序(每次只能交换相邻两个数),最小操作个数是逆序对数量之和。

因此对\(p\)数组求逆序对之和就行

\(Code\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#define re register
#define maxn 101010
#define ll long long
#define mod  99999997
#define ls p<<1
#define rs p<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
ll ans;
int len1,len2;
ll sums[maxn<<2];
int a[maxn],b[maxn],p[maxn],n;
struct PCC{
	int id,num;
}c[maxn],d[maxn];
bool cmp(PCC A,PCC B)
{
	if(A.num!=B.num)
	return A.num<B.num;
	
	return A.id<B.id;
}
void DCZ1()
{
	sort(c+1,c+n+1,cmp);
	for(re int i=1;i<=n;++i)
	 a[i]=c[i].id;	
} 
void DCZ2()
{
	sort(d+1,d+n+1,cmp);
    for(re int i=1;i<=n;++i)
     b[i]=d[i].id;
}
void push_up(int p)
{
	sums[p]=sums[ls]+sums[rs];
}
void insert(int p,int l,int r,int k)
{
	if(l==r)
	{
		sums[p]++;
		return ;
	}
	int mid=(l+r)>>1;
	if(k<=mid) insert(ls,l,mid,k);
	else insert(rs,mid+1,r,k);
	push_up(p);
}
int query(int p,int l,int r,int k)
{
	int tmp=0;
	if(l==r)
	{
		if(l<=k) tmp+=sums[p];
		return tmp;
	}
	int mid=(l+r)>>1;
	if(k>mid) tmp+=sums[ls],tmp+=query(rs,mid+1,r,k);
	else tmp+=query(ls,l,mid,k);
	return tmp; 
}
void solve()
{
	for(re int i=1;i<=n;++i)
	{
		ll tmp=(i-1)-query(1,1,n,p[i]);
		tmp=(tmp%mod+mod)%mod;
		ans=(ans+tmp)%mod;
		insert(1,1,n,p[i]);
	}
}
int main()
{
	n=read();
	for(re int i=1;i<=n;++i) a[i]=read(),c[i].num=a[i],c[i].id=i;
	for(re int i=1;i<=n;++i) b[i]=read(),d[i].num=b[i],d[i].id=i;
	DCZ1(),DCZ2();
	for(re int i=1;i<=n;++i)
	p[a[i]]=b[i];
	solve();
	printf("%lld\n",ans);	 	
	return 0;
}
posted @ 2019-10-26 10:48  __Liuz  阅读(112)  评论(0编辑  收藏  举报