标签:枚举 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));
标签:枚举 rac ac自动机 else -- max while 最大子段和 最大值
原文地址:https://www.cnblogs.com/cszmc2004/p/12781473.html