Luogu P11233 CSP-S2024 染色 題解 [ 藍 ] [ 線性 dp ] [ 字首和最佳化 ]

KS_Fszha發表於2024-11-22

染色:傻逼題。

賽時沒切染色的都是唐氏!都是唐氏!都是唐氏!都是唐氏!都是唐氏!都是唐氏!都是唐氏!

包括我。

真的太傻逼了這題。

我今晚心血來潮一打這題,隨便最佳化一下,就 AC 了。

怎麼做到這麼蠢的啊我。

暴力 dp

不要從子串的角度去考慮,我們對每一個數進行考慮 dp,否則會和我在場上一樣想成用區間設計 dp,並且只能打出 \(O(n^4)\) 的暴力出來。

設計 \(dp_{i}\) 表示考慮到第 \(i\) 個數的最大值。

這裡不用設計兩種顏色的狀態,因為他們本質上都是一樣的,我們分段時只注重前一段的開頭的前一個數,僅此而已。

那麼很顯然就可以打個 \(n^2\) 的暴力了吧?然後 \(50pts\) 到手,然後我考場上沒想出來,太傻逼了。

值域最佳化

顯然,我們不用列舉前一段的開頭的前一個數在哪,只需要知道它的值是什麼就好了。這樣就能根據字首取個最大值進行 dp 了。

設計 \(dp_{i,v}\) 表示考慮到第 \(i\) 個數,本段區間的開頭的前一個數是 \(v\) 時的最大答案。為啥是本段呢,因為下一段轉移的時候,這裡的本段就成了前一段。

於是顯然就維護一個線段樹轉移一下就好了啊。

有兩種轉移,第一個是同色轉移,第二個是不同色轉移。

同色轉移

條件:\(a_i=a_{i-1}\)

我們只要給線段樹上每一個點加上 \(a_i\) 即可。

異色轉移

條件:無。

顯然的轉移:

\[dp_{i,a_{i-1}}=\max(\max_{j=1}^{j=V_{max}} dp_{i-1,j},a_i+dp_{i-1,a_i}-[a_i=a_{i-1}]\times a_i) \]

就是要麼這一段開頭與前一段開頭的前一個數不相等,挑一個最大值轉移,要麼就是相等,加上貢獻即可,注意由於之前進行過同色轉移,要減掉這一部分的貢獻。(可以證明同色轉移之後一定是最大的,因為畢竟都把之前所有值的最大值加了 \(a_i\) 嘛)

時間 \(O(t n \log V)\),有機率被卡。

陣列最佳化

觀察到只有全域性最大值,全域性加,單點修改操作,於是直接把線段樹換成陣列省去一個老哥即可。

全域性加直接打懶標記就好。

由於懶標記一視同仁,所以比較加懶標記之前的 dp 值轉移,輸出時加上懶標記即可。

時間 \(O(tV)\),單組資料時間複雜度瓶頸在於初始化。

程式碼

/*
定義 dp_i,0/1 表示第 i 個數染色為 1 或 0 時的最大得分。
我們分別維護每種元素在考慮到第 i 個元素時充當上一段段尾的最大得分。

和上個數同色時:
當 i=i-1 時,此時前面的數里面全部加該點的點權。
否則不做任何改動。

這個數作為新開頭:
修改上一個元素在 dp 裡的值。
轉移是 dp_{i-1}=max(allmax,a_i+dp_{i}-[i==i-1]*a[i])
*/
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
ll n,a[200005],dp[1000005],add,mx=0;
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    memset(dp,-0x3f,sizeof(dp));
    add=0;
    mx=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]==a[i-1])add+=a[i];
        dp[a[i-1]]=max(mx,a[i]+dp[a[i]]-(a[i]==a[i-1])*a[i]);
        mx=max(mx,dp[a[i-1]]);
    }
    cout<<mx+add<<'\n';
}
int main()
{
    //freopen("color.in","r",stdin);
    //freopen("color.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--)solve();
    return 0;
}

相關文章