[BZOJ1584][Usaco2009 Mar]Cleaning Up 打扫卫生
Description
有N头奶牛,每头那牛都有一个标号Pi,1 <= Pi <= M <= N <= 40000。现在Farmer John要把这些奶牛分成若干段,定义每段的不河蟹度为:若这段里有k个不同的数,那不河蟹度为k*k。那总的不河蟹度就是所有段的不河蟹度的总和。
Input
第一行:两个整数N,M
第2..N+1行:N个整数代表每个奶牛的编号
Output
一个整数,代表最小不河蟹度
Sample Input
13 4
1
2
1
3
2
2
3
4
3
4
3
1
4
1
2
1
3
2
2
3
4
3
4
3
1
4
Sample Output
11
这题真的...打死我都想不到可以用这种方法优化...
首先设$f[i]$表示$i$之前的最优答案, 那么$f[i] = min(f[j]+dif^2)$。
这是$O(N^2)$的显然会T。
我们发现答案的最差值一定是$n$(每一个都当做一组),所以我们用来转移的区间只有不同颜色数量在$[1, \sqrt{N}]$之间。
然后就可以$O(N \sqrt{N})$转移了。
不太会写抄的网上的题解。
根号算法无处不在。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <bitset> #include <algorithm> using namespace std; #define reg register #define gc getchar inline int read() { int res=0;char ch=gc();bool fu=0; while(!isdigit(ch)){if(ch=='-')fu=1;ch=gc();} while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=gc(); return fu?-res:res; } #define N 40005 int n, m; int a[N], pre[N], nxt[N], pos[N], lst[N], cnt[N]; int f[N]; int main() { n = read(), m = read(); for (reg int i = 1 ; i <= n ; i ++) a[i] = read(), pre[i] = -1, nxt[i] = n + 1; for (reg int i = 1 ; i <= n ; i ++) { if (lst[a[i]]) pre[i] = lst[a[i]]; lst[a[i]] = i; } memset(lst, 0, sizeof lst); for (reg int i = n ; i >= 1 ; i --) { if (lst[a[i]]) nxt[i] = lst[a[i]]; lst[a[i]] = i; } memset(f, 0x3f, sizeof f); f[0] = 0; for (reg int i = 1 ; i <= n ; i ++) { for (reg int j = 1 ; j * j <= n ; j ++) if (pre[i] <= pos[j]) cnt[j]++; for (reg int j = 1 ; j * j <= n ; j ++) if (cnt[j] > j) { pos[j] ++; while(nxt[pos[j]] <= i) pos[j]++; cnt[j]--; } for (reg int j = 1 ; j * j <= n ; j ++) f[i] = min(f[i], f[pos[j]] + j * j); } cout << f[n] << endl; return 0; }