题解:AT_agc048_c [AGC048C] Penguin Skating

首先考虑这个题有个非常关键的转化,

考虑我这个东西是在移动, 然后配对位置,

那么每次滑动的时候一定是到不能动为止.

所以考虑这个东西的本质是每两个相邻东西之间的空白数量的转移,

并且一定有输出方完全输出所有空白.

对这个东西详细考虑.

令 \(x_i\) 表示 \(a_i\) 与 \(a_{i + 1}\) 之间的空格数量, \(y\) 表示 \(b\) 的.

一次操作:

  • 对 \(i\) 向右挪动到停止, 有 \(x_{i - 1} += x_i, x_i := 0\).

  • 对 \(i\) 向左挪动到停止, 有 \(x_i += x_{i - 1}, x_{i - 1} := 0\).

所以可以发现, 每个点 \(i\) 可以选择让其邻居 \(i - 1\) 或 \(i + 1\) 的 \(x\) 值加上 \(x_i\), 并将 \(x_i\) 置 \(0\).

然后这个题目就转化成了, 可以通过最少几次这种操作使得 \(x\) 数组与 \(y\) 数组完全相同.

分析, 每次对 \(x\) 数组的操作其实是将相邻两项类似 "合并",

因此最终的一处的 \(x_i\) 是由其所在的一个连续段构成的.

考虑 \(y_i\) 是由一段 \(x\) 得到的, 令它为 \(\{x_l, x_{l + 1}, \dots x_r\}\).

这一段的最小代价是 \(\max(i - l, 0) + \max(r - i, 0)\).

想一下, 不可能再少了, 而且这么多次内必然可以合并成一堆.

\(Code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

const int NR = 1e5 + 10;
long long a[NR], b[NR];
long long x[NR], y[NR];

int main()
{
    int n;
    long long m;
    scanf("%d%lld", &n, &m);
    for (int i = 1; i <= n; i ++)
    {
        scanf("%lld", &a[i]);
    }
    for (int i = 1; i <= n; i ++)
    {
        scanf("%lld", &b[i]);
    }
    a[0] = b[0] = 0, a[n + 1] = b[n + 1] = m + 1;
    for (int i = 0; i <= n; i ++)
    {
        x[i] = a[i + 1] - a[i] - 1;
        y[i] = b[i + 1] - b[i] - 1;
    }
    bool flag = true;
    long long ans = 0;
    for (int l = 0, i = 0; i <= n; i ++)
    {
        if (y[i] == 0) continue;
        while (l <= n && x[l] == 0) l ++;
        if (l > n) { flag = false; break; }
        int r = l;
        int sum = x[l];
        while (r <= n && sum < y[i])
        {
            r ++;
            sum += x[r];
        }
        if (r > n || sum != y[i]) { flag = false; break; }
        ans += max(i - l, 0) + max(r - i, 0);
        l = r + 1;
    }
    if (!flag) ans = -1;
    printf("%lld\n", ans);
    return 0;
}
posted @ 2026-03-12 17:56  hsy8116  阅读(12)  评论(0)    收藏  举报