Problem
给一段染色的代价是这一段不同颜色数的平方,求染完序列的最小代价。
Solution
动态转移方程显而易见,但是裸的DP会T。通过观察可以发现,当前枚举的断点如果可能成为答案断点的左边一定是右边这一区间没出现过的数。因此我们可以用链表维护,用每个数最新出现的位置更新链表,同时最终答案一点小于等于n,每次枚举只需枚举sqrt(i)次即可。
场上想不出优化时可以直接打一下转移的表观察看看。
Code
#include <iostream>
#include <map>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int n,a[50005],dp[50005],lef[50005],rig[50005];
int main()
{
while(scanf("%d",&n)!=EOF)
{
map<int,int>mp;
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=1; i<=n; i++)
{
lef[i]=i-1;
rig[i]=i+1;
}
lef[0]=-1;
dp[1]=1;
for(int i=2; i<=n; i++)
dp[i]=1e8;
for(int i=1; i<=n; i++)
{
if(mp[a[i]])
{
rig[lef[mp[a[i]]]]=rig[mp[a[i]]];
lef[rig[mp[a[i]]]]=lef[mp[a[i]]];
}
mp[a[i]]=i;
for(int j=lef[i],ans=1;~j&&ans*ans<=i; j=lef[j])
{
dp[i]=min(dp[i],dp[j]+ans*ans);
ans++;
}
}
cout<<dp[n]<<endl;
}
return 0;
}