P1966 [NOIP 2013 提高组] 火柴排队 解题报告

题目链接

题意

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

其中 \(a_i\) 表示第一列火柴中第 \(i\) 个火柴的高度,\(b_i\) 表示第二列火柴中第 \(i\) 个火柴的高度。

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

对于 \(100\%\) 的数据,\(1 \leq n \leq 10^5\)\(0 \leq a_i,b_i < 2^{31}\) 且对于任意 \(1\le i<j\le n\)\(a_i\neq a_j\)\(b_i\neq b_j\)

思路

首先注意到当 \(a\)\(b\) 都从小到大排一一对应是最优的。

然后考虑如何使每根火柴一一对应。

手玩一下不难发现交换次数是逆序对。

然后用树状数组求逆序对就可以了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, mod = 1e8 - 3;
struct node
{
	int id, a;
}a[N], b[N];
int n, w[N], ans, pre[N];
int lowbit(int x)
{
	return x & (-x);
}
bool cmp(node a, node b)
{
	return a.a < b.a;
}
void update(int x)
{
	for(; x <= n; x += lowbit(x))
	{
		w[x] ++;
	}
}
int query(int x)
{
	int cnt = 0;
	for(; x != 0; x -= lowbit(x))
	{
		cnt += w[x];
	}
	return cnt;
}
signed main()
{
	cin >> n;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i].a;
		a[i].id = i;
	}
	for(int i = 1; i <= n; i ++)
	{
		cin >> b[i].a;
		b[i].id = i;
	}
	sort(a + 1, a + n + 1, cmp);
	sort(b + 1, b + n + 1, cmp);
	for(int i = 1; i <= n; i ++)
	{
		pre[a[i].id] = b[i].id;
	}
	for(int i = 1; i <= n; i ++)
	{
		ans = (ans + i - 1 - query(pre[i]))% mod;
		update(pre[i]);
	}
	cout << ans << endl;
}
posted @ 2026-06-05 10:10  Luowj  阅读(9)  评论(0)    收藏  举报