题解: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;
}

浙公网安备 33010602011771号