链接
http://acm.hdu.edu.cn/showproblem.php?pid=5009
题解
乱搞题
fi=min{fj−1+num2(j,i)}
f
i
=
m
i
n
{
f
j
−
1
+
n
u
m
2
(
j
,
i
)
}
咋优化?
看着像斜率优化,但问题是凸壳会变啊,难道用平衡树维护吗?我反正不想写
那只好瞎搞搞了
看这个:
2323232323232323232323
显然我从一个位置往前枚举的时候,
num(j,i)
n
u
m
(
j
,
i
)
总是不变的,但是
f[j−1]
f
[
j
−
1
]
在变小,所以我应该取
num
n
u
m
相同的里面最往前的一个
我在进行枚举的时候,如果发现往前前进一步时
num(j,i)
n
u
m
(
j
,
i
)
却不变,那么这个元素的存在就是没意义的,我可以直接删掉,这个可以用一个双向链表来实现,而且当我的
num(j,i)
n
u
m
(
j
,
i
)
大于
N−−√
N
时,我就没必要枚举下去了
时间复杂度
O(NN−−√)
O
(
N
N
)
,这里的事件复杂度是指最劣的情况,即每个数字都不相同
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <cmath>
#include <cstdlib>
#define maxn 50010
#define ll_inf (1ll<<60)
#define ll long long
#define cl(x,y) memset(x,y,sizeof(x))
using namespace std;
ll f[maxn], N, a[maxn], M, vis[maxn], tmp[maxn], pre[maxn], nex[maxn], flag, sqr[maxn];
ll read(ll x=0)
{
char c, f=1;
for(c=getchar();!isdigit(c) and c^-1;c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
return f*x;
}
bool init()
{
ll i, t=0;
cl(f,0);
N=read();
if(!N)return 0;
for(i=1;i<=N;i++)tmp[i]=a[i]=read();
sort(tmp+1,tmp+N+1);
for(i=1;i<=N;i++)if(tmp[i]!=tmp[i-1])flag++;
for(i=1;i<=N;i++)a[i]=lower_bound(tmp+1,tmp+N+1,a[i])-tmp;
for(i=1;i<=N;i++)if(a[i]!=a[i-1])a[++t]=a[i];
for(i=1;i<=N;i++)sqr[i]=i*i;
N=t;
cl(vis,0);
return 1;
}
void dp()
{
ll i, j, cost, pos;
pre[1]=0;
for(i=1;i<=N;i++)
{
cost=0;
f[i]=ll_inf;
pre[i]=i-1, nex[i-1]=i;
for(j=i;j and sqr[cost]<=i;j=pre[j])
{
if(vis[a[j]]!=i)cost++, vis[a[j]]=i;
else pre[nex[j]]=pre[j], nex[pre[j]]=nex[j];
f[i]=min(f[i],sqr[cost]+f[pre[j]]);
}
}
printf("%lld\n",f[N]);
}
int main()
{
while(init())dp();
return 0;
}