AT_abc338_d 题解

· · 题解

题意简述

有一个环,要按给定的顺序经过环上的一些节点,任选环上的一条边删除,最少需要经过几条边走完。

思路

先考虑如果不用删边,最短的路径应该怎么求。

一个点到下一个点,要么顺时针走,要么逆时针走,统计两者的较小值即可。

我们不去想删除一条边后的路径该怎么求,而要去想删哪条边的代价最小。

我们假设不删边时,顺时针走总是比逆时针走更优。

那么我们如果改成走逆时针,那么就会多出这两者之差的代价。

我们删掉顺时针走的路径上的任意一条边,都会导致我们只能转而去走逆时针。

显而易见的,我们可以使用差分,将更优的路径上的所有边都加上两个选择的差。

最后被加的数最小的边就是最优解,答案就是不删边的答案加上那条边被加的数。

Code

#include <iostream>
#include <cmath>
using namespace std;
const int N = 200005;
typedef long long LL;
int x[N], d1[N], d2[N];
LL d[N];
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i=1;i<=m;i++)
        scanf("%d", &x[i]);
    LL ans = 0;
    for (int i=1;i<m;i++)
    {
        d1[i] = x[i + 1] - x[i];
        if (d1[i] <= 0)
            d1[i] += n;
        d2[i] = x[i] - x[i + 1];
        if (d2[i] <= 0)
            d2[i] += n;
        ans += min(d1[i], d2[i]);
        const int tmp = abs(d1[i] - d2[i]);
        if (d1[i] < d2[i])
        {
            d[x[i]] += tmp;
            if (x[i] < x[i + 1])
                d[x[i + 1]] -= tmp;
            else
            {
                d[1] += tmp;
                d[x[i + 1]] -= tmp;
            }
        }
        else if (d1[i] > d2[i])
        {
            d[x[i + 1]] += tmp;
            if (x[i + 1] < x[i])
                d[x[i]] -= tmp;
            else
            {
                d[1] += tmp;
                d[x[i]] -= tmp;
            }
        }
    }
    for (int i=2;i<=n;i++)
        d[i] += d[i - 1];
    LL mn = 9e18;
    for (int i=1;i<=n;i++)
        mn = min(mn, d[i]);
    cout << ans + mn;
    return 0;
}