bzoj1584【Usaco2009 Mar】Cleaning Up 打扫卫生

来源:互联网 发布:韩国淘宝美女模特官网 编辑:程序博客网 时间:2024/04/27 08:45

1584: [Usaco2009 Mar]Cleaning Up 打扫卫生

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 390  Solved: 265
[Submit][Status][Discuss]

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

HINT

Source

Gold




思路很棒的DP题

可以发现每一段中不同的数的个数不超过sqrt(n)个。

所以我们只需要记录每个点为结尾,一段序列中不同的数的个数不超过j的左端点的位置-1,pos[j]。通过pos数组进行DP转移,可以将复杂度从O(n^2)将为O(sqrt(n)n)。f[i]=min{f[pos[j]]+j*j}。

下面我们考虑右端点右移,也就是i++时,如何更新pos数组。

为了快速更新,我们还需要记录每个数值i的最后一个位置pre[i],和以pos[j]为左端点的序列中不同的数的个数cnt[j]。

当i++后,如果pre[a[i]]≤pos[j],那么cnt[j]++,说明序列中加入一个新的元素。

然后我们找出所有cnt[j]>j的序列,也就是不满足条件的序列,适当地调整左端点pos[j],使其满足cnt[j]≤j。其中对于左端点的调整只需要暴力即可,这是均摊O(1)的。

所以这道题最终的时间复杂度为O(sqrt(n)n)。

P.S.可能写的不太清楚…如果不懂还是看程序吧 QAQ

P.P.S.题目样例有问题...应该是10...坑爹啊 =。=




#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#define F(i,j,n) for(int i=j;i<=n;i++)#define D(i,j,n) for(int i=j;i>=n;i--)#define ll long long#define maxn 40005using namespace std;int n,m;int a[maxn],f[maxn],pre[maxn],pos[maxn],cnt[maxn];inline int read(){int x=0,f=1;char ch=getchar();while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}int main(){n=read();m=read();m=int(sqrt(n));F(i,1,n) a[i]=read();F(i,0,n) f[i]=i;memset(pre,-1,sizeof(pre));F(i,1,n){F(j,1,m) if (pre[a[i]]<=pos[j]) cnt[j]++;pre[a[i]]=i;F(j,1,m) if (cnt[j]>j){int t=pos[j]+1;while (pre[a[t]]>t) t++;pos[j]=t;cnt[j]--;}F(j,1,m) f[i]=min(f[i],f[pos[j]]+j*j);}printf("%d\n",f[n]);}


0 0
原创粉丝点击