序列(sequence)

题目链接: 序列(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(aibi)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 n300000,表示序列长度。 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 1ai109,1in

第三行 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 1bi109,1in

输出描述

第一个数,输出两个序列的最小距离对 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(aibi)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;
}

Alt

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值