有N头奶牛,每头那牛都有一个标号Pi,1 <= Pi <= M <= N <= 40000。现在Farmer John要把这些奶牛分成若干段,定义每段的不河蟹度为:若这段里有k个不同的数,那不河蟹度为k*k。那总的不河蟹度就是所有段的不河蟹度的总和。
有N头奶牛,每头那牛都有一个标号Pi,1 <= Pi <= M <= N <= 40000。现在Farmer John要把这些奶牛分成若干段,定义每段的不河蟹度为:若这段里有k个不同的数,那不河蟹度为k*k。那总的不河蟹度就是所有段的不河蟹度的总和。
第一行:两个整数N,M
第2..N+1行:N个整数代表每个奶牛的编号
一个整数,代表最小不河蟹度
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 40005 using 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]); }