[Usaco2009 Mar]Cleaning Up

来源:互联网 发布:模拟期货交易软件 编辑:程序博客网 时间:2024/04/26 08:36

有N头奶牛,每头那牛都有一个标号Pi,1 <= Pi <= M <= N <= 40000。 现在Farmer John要把这些奶牛分成若干段,定义每段的不河蟹度为:若这段里有k个不同的数,那不河蟹度为k*k。那总的不河蟹度就是所有段的不河蟹度的总和


这里的M貌似没什么用。

分成若干段是想分多少段就多少段。


首先需要发现这个问题。

如果在某段区间中,不同的数超过了sqrt(n)个, 那么很显然,我们还不如将整个区间分为n段。

那么我们就不用管区间不同数超过sqrt(n)的数了。

令数组b[j]表示 从b[j] + 1到i 不同的数的数量不超过j的最左端

也就是说a[b[j]] + 1]到 a[i]之间的数不超过j个

那么转移方程如下

f[i] = min(f [ b[j] ] + j * j, f[ i ] ) 1 <= j <= sqrt(n)

那么这个b[j]怎么维护呢

令c[j]表示从b[j]+1下标到i的区间中不同数的个数。

对于新加入的一个数.

如果该数之前出现的位置x 不大于b[j],那么说明b[j] +1到i中不同数的数量要+1

然后计算完之后

可能有c[j] > j的,这显然是不应该的。

那么就要从区间头部删掉数使得c[j]不大于j。 并且要更新b[j]

这个过程实际上就是一个游标右移,直至游标左边去掉的某个数确实在游标右边不存在了。

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <algorithm>#include <cstdlib>#include <cmath>#include <map>#include <sstream>#include <queue>#include <stack>#include <vector>#define MAXN 111111#define MAXM 211111#define PI acos(-1.0)#define eps 1e-8#define INF 100000001using namespace std;int p[55555];int a[55555], b[55555], c[55555], dp[55555];int n, m, k;int main(){    memset(dp, 0x3f, sizeof(dp));    memset(p, -1, sizeof(p));    scanf("%d%d", &n, &m);    int k = (int)sqrt(n + 0.5);    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);    dp[0] = 0;    for(int i = 1; i <= n; i++)    {        for(int j = 1; j <= k; j++)            if(p[a[i]] <= b[j]) c[j]++;        p[a[i]] = i;        for(int j = 1; j <= k; j++)            if(c[j] > j)            {                int now = b[j] + 1;                while(p[a[now]] > now) now++;                b[j] = now;                c[j]--;            }        for(int j = 1; j <= k; j++)            dp[i] = min(dp[i], dp[b[j]] + j * j);    }    printf("%d\n", dp[n]);    return 0;}


原创粉丝点击