考虑 (DP),设 (f_i) 为考虑了前 (i) 张牌的最大总分,(a_i) 为第 (i) 张牌的种类,(s_i) 为前 (i) 张牌和第 (i) 张牌的种类相同的牌的个数,得转移为:
[large f_i=max_{a_j=a_i}left { f_{j-1}+left( s_i-s_j+1
ight)^{frac{1}{2}k}
ight }
]
直接转移是 (O(n^2)) 的,需要考虑优化。发现对于种类相同的牌之间的转移具有决策单调性,即转移 (j,k),其中 (j<k),若转移 (j) 比转移 (k) 更优,会一直比其优,原因是转移的第二项关于 (s_i) 的导数恒为正值。
对于每种牌维护一个单调栈,每次插入时二分相邻项中前一项代替后一项的时间即可更新单调栈。
#include<bits/stdc++.h>
#define maxn 1000010
#define siz(x) (ve[x].size())
#define ve(x,y) (ve[x][ve[x].size()-y])
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int k,n;
int a[maxn],cnt[maxn],s[maxn];
double v[maxn],f[maxn];
vector<int> ve[maxn];
double calc(int x,int sum)
{
return f[x-1]+v[sum];
}
int get(int i,int j)
{
int l=1,r=n,pos=n+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(calc(i,mid-s[i]+1)>=calc(j,mid-s[j]+1)) pos=mid,r=mid-1;
else l=mid+1;
}
return pos;
}
int main()
{
read(k),read(n);
for(int i=1;i<=n;++i)
{
double t=1;
for(int j=1;j<=k;++j) t*=i;
v[i]=sqrt(t),read(a[i]),s[i]=++cnt[a[i]];
}
for(int i=1;i<=n;++i)
{
int x=a[i];
while(siz(x)>1&&get(ve(x,2),ve(x,1))<=get(ve(x,1),i)) ve[x].pop_back();
ve[x].push_back(i);
while(siz(x)>1&&get(ve(x,2),ve(x,1))<=s[i]) ve[x].pop_back();
f[i]=calc(ve(x,1),s[i]-s[ve(x,1)]+1);
}
printf("%.10lf",f[n]);
return 0;
}