Codeforces 2153D Not Alone 题解 [ 绿 ] [ 线性 DP ] [ 分类讨论 ]

Not Alone:唐题。

容易将题意转化为:环上的每一个颜色段长度都 \(\ge 2\),求最小操作数。

再考虑一个 \(O(n^2\log n)\) 的暴力,定义 \(dp_i\) 表示以 \(i\) 结尾的最小操作数,然后枚举前一个转移点 \(j\)。继续考虑如何计算 \([j + 1, i]\) 的最小操作数,容易注意到这是个经典的绝对值不等式,于是取中位数即可。

感性地发现一大段相等的数不如拆成几个小段的区间。于是从 \(n = 2, 3,4\) 的情况入手,发现:

  • \(n = 2\) 时,只能全部相等。
  • \(n = 3\) 时,只能全部相等。
  • \(n = 4\) 时,\([a_1, a_2, a_3, a_4]\) 分成 \([a_1, a_2],[a_3, a_4]\) 比直接不分段更优秀,因为分段时的代价就是 \(|a_2 - a_1| + |a_4 - a_3|\),不分段的时候,分类讨论中位数的位置发现最小值才是 \(|a_2 - a_1| + |a_4 - a_3|\),甚至有可能比它更大。因此不分段一定不优。
  • \(n = 5, 6, 7, \cdots\) 时,类似 \(n = 4\),考虑数学归纳法,发现分成若干个长度为 \(2, 3\) 的段一定最优。

于是线性 DP 求解最小方案即可。

拓展到环上,破环为链复杂度会爆炸,所以只需要分类讨论首尾相接处的情况即可。详见代码,时间复杂度 \(O(n)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 400005;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll n, a[N], ans, res, dp[N];
ll work(int l, int r)
{
    dp[l - 1] = 0;
    for(int i = l; i <= r; i++)
    {
        dp[i] = inf;
        if(i == l) continue;
        // 2
        dp[i] = min(dp[i], dp[i - 2] + abs(a[i] - a[i - 1]));
        // 3
        if(i == l + 1) continue;
        int mx = max(a[i], max(a[i - 1], a[i - 2])), mn = min(a[i], min(a[i - 1], a[i - 2]));
        int md = (a[i] ^ a[i - 1] ^ a[i - 2] ^ mx ^ mn);
        dp[i] = min(dp[i], dp[i - 3] + mx - md + md - mn);
    }
    return dp[r];
}
void solve()
{
    cin >> n;
    ans = inf;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    // 0 + 0
    ans = min(ans, work(1, n));
    // 1 + 1
    if(n > 3)
        ans = min(ans, work(2, n - 1) + abs(a[1] - a[n]));
    // 1 + 2
    if(n > 4)
    {
        int mx = max(a[1], max(a[n], a[n - 1])), mn = min(a[1], min(a[n - 1], a[n]));
        int md = (a[1] ^ a[n - 1] ^ a[n] ^ mx ^ mn);
        ans = min(ans, work(2, n - 2) + mx - md + md - mn);
    }
    // 2 + 1
    if(n > 4)
    {
        int mx = max(a[1], max(a[n], a[2])), mn = min(a[1], min(a[2], a[n]));
        int md = (a[1] ^ a[2] ^ a[n] ^ mx ^ mn);
        ans = min(ans, work(3, n - 1) + mx - md + md - mn);
    }
    cout << ans << "\n";
}
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--) solve();
    return 0;
}
posted @ 2025-10-11 01:15  KS_Fszha  阅读(64)  评论(0)    收藏  举报