洛谷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 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;
}
原文地址:https://www.cnblogs.com/lhm-/p/14253409.html