洛谷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;
}
posted @ 2021-01-08 21:10  lhm_liu  阅读(162)  评论(0编辑  收藏  举报