题解:P16499 【MX-S14-T2】「KWOI R2」染色

题目传送门

思路

动态规划

状态定义

\(dp_{i,c}\) 表示:前 \(i\) 个元素,第 \(i\) 个元素被分到子序列 \(c\) 时,能获得的最大总得分。其中 \(c\)\(0\)\(1\),表示红和蓝。

转移方程

对于第 \(i\) 个元素 \(A_i\),它存在两种情况。

情况一:\(A_i\) 和上一个同色元素相连

假设第 \(i\) 个元素和第 \(j\) 个元素同色(\(j<i\)),且 \(A_j\) 是子序列 \(c\) 中在 \(A_i\) 左边,离 \(A_i\) 最近的元素,即当前与 \(A_i\) 相邻,则有:

\[dp_{i,c}=dp_{j,c}+|A_i-A_j| \]

情况二:\(A_i\) 是当前颜色子序列的第一个元素

\(A_i\) 无法造成额外贡献,直接转移:

\[dp_{i,c}=\max_{k<i}\{dp_{k,1-c}\} \]

优化

观察数据范围,这样的转移显然会超时,考虑优化。

观察转移方程:

\[dp_{i,c}=\max_{j<i}\{dp_{j,c}+|A_i-A_j|\} \]

将绝对值拆开得:

  • \(A_j \leq A_i\)\(dp_{j,c}+|A_i-A_j|=dp_{j,c}+A_i-A_j=(dp_{j,c}-A_j)+A_i\)
  • \(A_j > A_i\)\(dp_{j,c}+|A_i-A_j|=dp_{j,c}+A_j-A_i=(dp_{j,c}+A_j)-A_i\)

所以我们只需要维护两个最大值:

  • \(m=\max\{dp_{j,c}-A_j\}\)
  • \(p=\max\{dp_{j,c}+A_j\}\)

于是转移方程变成了:

\[dp_{i,c}=\max(m+A_i,p-A_i) \]

时间复杂度就变成线性的了,刚好通过数据。

代码

在实现中,其实可以不用数组存 \(dp_{i,0}\)\(dp_{i,1}\),观察可得以下算法实现:

  • 每步转移实际上是在维护一个额外得分 \(v\)
  • \(m\)\(p\) 同上文维护最大值。
  • \(ans\) 累加所有 \(|A_i-A_{i-1}|\) 作为基本得分。
  • \(maxn\) 为过程中所有 \(v\) 的最大值,则答案为 \(ans+maxn\)
AC Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int cid,test;
namespace FastIO {
    char buf[1<<21], *p1=buf, *p2=buf;
    inline int getch (void) {
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
    }
    inline int read (void) {
        int x = 0, f = 1, ch = getch();
        while(!isdigit(ch)) {
            if(ch == '-') f = -f;
            ch = getch();
        }
        while(isdigit(ch)) {
            x = x * 10 + ch - '0';
            ch = getch();
        }
        return x * f;
    }
    char buf2[1<<21], buf3[25];
    int tp_l, buf2_l=-1;
    inline void flush (void) {
        fwrite (buf2, 1, buf2_l+1, stdout), buf2_l=-1;
    }
    inline void print (long long x, char ch=10) {
        if(buf2_l>(1<<20)) flush();
        if(x<0) buf2[++buf2_l]='-', x=-x;
        do buf3[++tp_l]=x%10+48;
        while(x/=10);
        do buf2[++buf2_l]=buf3[tp_l];
        while(--tp_l);
        buf2[++buf2_l] = ch;
    }
}
using FastIO::read;
using FastIO::print;
int n,x,y,ans,maxn,mm,mp,b,t,v;
void solve()
{
    n=read();
    if(n==1)
    {
        read();
        print(0);
        return;
    }
    x=read();
    ans=0,maxn=0,mm=-1e18,mp=-1e18;
    for(int i=2;i<=n;i++)
    {
        y=read();
        t=abs(y-x);
        b=max(mm+y,mp-y);
        v=max(b,0ll);
        v-=t,ans+=t;
        maxn=max(maxn,v);
        mm=max(mm,v-x);
        mp=max(mp,v+x);
        x=y;
    }
    print(ans+maxn);
}
signed main(){
    cid=read();test=read();
    while(test--){
        solve();
    }
    FastIO::flush();
    return 0;
}
posted @ 2026-05-16 20:10  kevin1426730  阅读(4)  评论(0)    收藏  举报