求逆序对+思维 火柴排队(洛谷 P1966)

火柴排队

题目描述

涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为:∑(ai−bi)^2

其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。

每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 10^8-3 取模的结果。

输入格式

共三行,第一行包含一个整数 n,表示每盒中火柴的数目。

第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。

第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

输出格式

一个整数,表示最少交换次数对 10^8-3 取模的结果。


首先要推这个式子 ∑(ai−bi)^2=∑(ai ^2+bi ^2-2aibi) 可以发现ai ^2+bi ^2总和是不变,只要找到ai*bi的最大值;

可以发现,当ai和bi都是升序排列的时候,ai*bi是最大的;

所以可以把ai和bi都离散化以后,以ai为序列当做升序模板,把bi的排列变为ai的排列顺序;

可以想到这里就是求逆序对了;

很多求解法,我用的是权值线段树;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100010;
const int M=2000100;
const LL mod=1e8-3;
int n,a[N],a1[N],b[N],b1[N],sum,pos[N];
LL ans;
struct Node{
	int l,r,w;
}tr[N*4];
void build(int l,int r,int k){
	tr[k].l=l,tr[k].r=r;
	if(l==r){
		tr[k].w=0;
		return;
	}
	int d=(l+r)>>1;
	build(l,d,ls);
	build(d+1,r,rs);
	tr[k].w=tr[ls].w+tr[rs].w;
}
void add(int p,int k){
	if(tr[k].l==tr[k].r){
		tr[k].w++;
		return;
	}
	int d=(tr[k].l+tr[k].r)>>1;
	if(p<=d) add(p,ls);
	else add(p,rs);
	tr[k].w=tr[ls].w+tr[rs].w;
}
void query(int ll,int rr,int k){
	if(tr[k].l>=ll&&tr[k].r<=rr){
		sum+=tr[k].w;
		return;
	}
	int d=(tr[k].l+tr[k].r)>>1;
	if(ll<=d) query(ll,rr,ls);
	if(rr>d) query(ll,rr,rs);
}
int main(){	
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),a1[i]=a[i];
	for(int i=1;i<=n;i++) scanf("%d",&b[i]),b1[i]=b[i];
	sort(a1+1,a1+1+n),sort(b1+1,b1+1+n);
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(a1+1,a1+n+1,a[i])-a1;
		b[i]=lower_bound(b1+1,b1+n+1,b[i])-b1;
	}
	for(int i=1;i<=n;i++) pos[a[i]]=i;
	build(1,n,1);
	for(int i=1;i<=n;i++){
		sum=0;
		query(pos[b[i]]+1,n,1);
		(ans+=sum)%=mod;
		add(pos[b[i]],1);
	}
	cout<<ans<<endl;
	return 0;
}

发布了264 篇原创文章 · 获赞 46 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44291254/article/details/105206747