洛谷P6674 [CCO2019] Card Scoring
考虑 \(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 \right)^{\frac{1}{2}k} \right \}
\]
直接转移是 \(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;
}