1. 程式人生 > >[BZOJ1584][Usaco2009 Mar]Cleaning Up 打掃衛生

[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

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; }