卧薪尝胆,厚积薄发。
USACO2009MAR GOLD 打扫卫生
Date: Wed Sep 05 11:17:09 CST 2018
In Category:
NoCategory
Description:
有
$n$
个数字,每一段的不和谐度是这个区间内不同数字个数的平方,求一种划分方案最小化不和谐度的和。
$1\le n \le 40000$
Solution:
首先如果每一个数划分为一段,那么总的不和谐度就是
$n$
,所以最终的最小不和谐度一定不大于
$n$
,也就是说一段最多只能有
$\sqrt n$
个不同的数字,又看到这题数据范围,复杂度应该是
$O(n\sqrt n)$
。
$f[i]$
表示划分到
$i$
的代价,
$b[j]$
为从
$b[j]+1$
到
$i$
共有
$j$
种数字的最靠前的位置,因为同样数字种数,越长肯定有可能更优,
$c[j]$
表示
$b[j]+1$
到
$i$
的数字种数,用来维护
$b$
,
$pre[a[i]]$
表示数字
$a[i]$
的上一个位置。
先说转移,
$f[i]=\min(f[b[j]]+j\times j)$
。
然后问题就变成了如何维护
$b$
数组,当
$i$
后移一位时,如果之前
$b[j]+1$
到
$i-1$
这段区间中没有出现过,这个可以用
$pre$
判断,那么
$++c[j]$
,如果
$c[j]>j$
,就不断后移
$b[j]$
直到删去了一个只在
$[b[j]+1,i]$
中出现了一次的数字,这个也可以用
$pre$
判断。
一般不同数字个数的题都要维护一个
$pre$
。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n,m;
#define MAXN 40010
int a[MAXN];
int f[MAXN];
int b[MAXN],c[MAXN];
int s[MAXN],tot = 0;
int pre[MAXN];
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
memset(f,0x3f,sizeof(f));
f[0] = 0;
m = sqrt(n);
for(int i = 1;i <= n;++i)
{
for(int j = 1;j <= m;++j)
if(pre[a[i]] <= b[j])
++c[j];
pre[a[i]] = i;
for(int j = 1;j <= m;++j)
{
if(c[j] > j)
{
++b[j];
while(pre[a[b[j]]] > b[j])++b[j];
c[j] = j;
}
}
for(int j = 1;j <= m;++j)
f[i] = min(f[i],f[b[j]] + j * j);
}
cout << f[n] << endl;
return 0;
}
In tag:
DP-DP
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡