码迷,mamicode.com
首页 > 其他好文 > 详细

GDOI2019 小说

时间:2020-04-26 19:02:36      阅读:117      评论:0      收藏:0      [点我收藏+]

标签:枚举   rac   ac自动机   else   --   max   while   最大子段和   最大值   

神题。。。。。

这道题的60分做法是:分答案串<=100和>100讨论。

<=100的部分可以使用算法3解决。枚举一个点l表示现在要统计左端点l~l+99所有前缀字符串的密度的最大值。可以使用ac自动机解决

>100的部分可以二分+ac自动机。

原问题实际上是一个分数规划问题。假设选了i个物品,编号a[1],a[2]...a[i],则平均值是$\frac{a[1]+a[2]+....+a[i]}{i}$

如果设一个二分值mid,判定答案是否>=mid,则实际上是判定$a[1]-md+a[2]-md+...+a[i]-md>=0$,实际上就是选一个位置有md的代价,要求最大值。

使用dp解决。设f[i]表示i结尾的串,长度要>100的最大价值。ans[i]表示i-99~i的以i结尾的子串价值的最大值,st[i]表示ac机上以i结尾的串的价值,ans可以使用算法3计算。

f的转移有点像最大子段和。设v表示1~i-100的前缀最大价值,则v=max(v+st[i-100]-md,0),表示枚举是否选i-100这个点。

f[i]=max(f[i],v+ans[i]-100*md)表示使用i-99~i和1~i-100的最大价值组合答案。

只需要判定max(f[1~n])是否>=0即可。

核心代码:

int ck(double md){
    double r=-1e18,v=0;
    for(int i=100;i<n;i++){
        v=max(v+st[i-100]-md,0.0);
        r=max(r,v+ans[i]-100.0*md);
    }
    return r>=0;
}
double va=0;
for(int i=0;i<n;i++){
    int x=0,ss=0;
    for(int j=i;j<min(i+100,n);j++){
        x=a.c[x][s[j]-a];
        ss+=a.f[x];
        va=max(va,ss/((double)j-i+1));
    }
    if(i+99<n)ans[i+99]=ss;
}
int x=0;
for(int i=n-1;i;i--){
    x=b.c[x][s[i]-a];
    st[i]=b.f[x];
}
double l=0,r=1000000000;
while(l+1e-6<r){
    double md=(l+r)*0.5;
    if(ck(md))l=md;
    else r=md;
}
printf("%.4lf\n",max(l,va));

 

GDOI2019 小说

标签:枚举   rac   ac自动机   else   --   max   while   最大子段和   最大值   

原文地址:https://www.cnblogs.com/cszmc2004/p/12781473.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!