题目:有n个珠子需要染上特定的颜色,初始的时候是没有染色的,每次染的代价是染色区间中不同颜色数量的平方。
思路:最极端的情况是每次拿一个点染色花费1,最终花费为n,所以这个题目最后dp[n]是不会超过n的,往前找转移的点时候只需要往前找到最多产生sqrt(n)的位置就可以了,因为再往前也是徒劳。维护每个数字出现的最后一次的位置,然后中间有些位置是空的啦,用双向链表维护前面那个不同数字最后出现在哪,后面的出现在哪,不断跟新就可以了。这里用我用数组模拟的链表写挺繁琐的,不过能过题就好啦。
一开始拿到这个题目时,先想的是用线段树来优化,想不到。然后我就去做B题了,做完B后再看这个题突然有了思路emmm,中间有点难写,debug好长一会然后写完a了。训练完后看题解思路一样但是人家的代码好短呀。。下面是本菜鸡比较长的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+10;
int n,a[maxn],dp[maxn],l[maxn],r[maxn],b[maxn],las[maxn];
int main()
{
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
{
dp[i]=n;
l[i]=r[i]=0;
las[i]=0;
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
int o=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+o+1,a[i])-b;
dp[1]=1;
las[a[1]]=1;
l[1]=0;r[1]=n;
for(int i=2;i<=n;i++)
{
if(las[a[i]])
{
int k=las[a[i]];
l[r[k]]=l[k];
r[l[k]]=r[k];
las[a[i]]=i;
r[i]=n;
if(a[i-1]!=a[i])
{
l[i]=i-1;
r[i-1]=i;
}
else
{
l[i]=l[i-1];
r[l[i-1]]=i;
}
}
else
{
las[a[i]]=i;
r[i-1]=i;
l[i]=i-1;
r[i]=n;
}
int k=1;
for(int j=l[i];k<=sqrt(i)&&j>=0;j=l[j])
{
dp[i]=min(dp[i],dp[j]+k*k);
k++;
}
}
printf("%d\n",dp[n]);
}
return 0;
}