题目链接: 序列(sequence)
时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 256MB,其他语言 512MB
描述
a, b 为两个长度为 n 的序列,定义它们的距离为 ∑ i = 1 n ( a i − b i ) 2 \sum_{i=1}^{n} (a_i - b_i)^2 ∑i=1n(ai−bi)2。
现在给你两个序列 a 和 b,你可以任意交换 a 中的两个元素。
请你求出经过若干次交换之后,两个序列的最小距离,对 998244353 取模。
并求出最少的交换步数。
保证 a, b 序列中没有相同的元素,即 i ≠ j i \neq j i=j 则 a i ≠ a j , b i ≠ b j a_i \neq a_j, b_i \neq b_j ai=aj,bi=bj。
输入描述
第一行两个数字 n, T,分别表示序列长度和任务类型,其中 n ≤ 300000 n \leq 300000 n≤300000,表示序列长度。 T ∈ { 0 , 1 } T \in \{0, 1\} T∈{0,1}, 表示任务类型。
第二行 n 个整数 a i a_i ai,其中 1 ≤ a i ≤ 1 0 9 , 1 ≤ i ≤ n 1 \leq a_i \leq 10^9, 1 \leq i \leq n 1≤ai≤109,1≤i≤n。
第三行 n 个整数 b i b_i bi,其中 1 ≤ b i ≤ 1 0 9 , 1 ≤ i ≤ n 1 \leq b_i \leq 10^9, 1 \leq i \leq n 1≤bi≤109,1≤i≤n。
输出描述
第一个数,输出两个序列的最小距离对 998244353 取模的结果。
如果 T = 0 T = 0 T=0,则只输出第一个数,否则,第二个数输出最小交换次数。两个数用空格分开。
思路分析
-
距离计算:
定义两个序列 a a a 和 b b b 的距离为 ( ∑ i = 1 n ( a i − b i ) 2 ) (\sum_{i=1}^{n} (a_i - b_i)^2) (∑i=1n(ai−bi)2)。
我们需要通过交换 a a a 中的元素来最小化这个距离。 -
最小化距离:
由于 a a a 和 b b b 中的元素都是唯一的,我们可以通过将 a a a 中的元素重新排列,使得 a i a_i ai 尽可能接近 b i b_i bi。
具体来说,我们可以将 a a a 和 b b b 中的元素按相同的顺序排序,这样 a i a_i ai 和 b i b_i bi 的对应关系就是最优的。 -
计算最小交换次数:
为了计算最少的交换次数,我们需要找到 a a a 中的元素与 b b b 中的元素之间的置换环。
每个置换环的长度减去 1 1 1 就是该环需要的交换次数。
总的交换次数就是所有置换环的交换次数之和。
具体步骤
-
读取输入:
- 读取 n n n 和 T T T。
- 读取序列 a a a 和 b b b。
-
计算最小距离:
- 将 a a a 和 b b b 分别排序。
- 计算排序后的 a a a 和 b b b 的距离。
-
计算最小交换次数如果 T = 1 T = 1 T=1:
- 构建一个映射,将 a a a 中的每个元素映射到 b b b 中的对应位置。
- 使用深度优先搜索(DFS)或并查集来找到所有的置换环。
- 计算每个置换环的长度,并求出总的交换次数。
代码实现
#include <bits/stdc++.h>
using namespace std;
const int N = 6e5 + 5;
const long long MOD = 998244353;
int n, t;
pair<int, int> a[N], b[N];
int p[N];
int main()
{
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
scanf("%d %d", &n, &t);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &a[i].first);
a[i].second = i;
}
for (int i = 1; i <= n; ++i)
{
scanf("%d", &b[i].first);
b[i].second = i;
}
sort(a + 1, a + n + 1),sort(b + 1, b + n + 1);
long long ans = 0;
for (int i = 1; i <= n; ++i)
{
p[a[i].second] = b[i].second; // 将序列 A 中的元素对应到序列 B 中的位置
ans = (ans + (long long)(a[i].first - b[i].first) * (a[i].first - b[i].first)) % MOD;
}
long long sum = 0;
for (int i = 1; i <= n; ++i)
{
while (p[i] != i) // 如果当前位置的对应不是自己
{
swap(p[i], p[p[i]]); // 交换对应关系,使其正确
sum++;
}
}
printf("%lld ", ans);
if (t == 1)
{
printf("%lld\n", sum);
}
return 0;
}