Description
n
<
=
1
0
5
,
∑
∣
B
i
∣
<
=
2
e
5
n<=10^5,\sum|Bi|<=2e5
n<=105,∑∣Bi∣<=2e5
- 原题GDSOI2019 D2 T3 Novel
Solution
- 相当巧妙的AC自动机上的DP。
- 首先对于这一类分数规划的问题一般都要二分答案,然后化一下式子变成 W l , r − m i d ∗ l e n > = 0 W_{l,r}-mid*len>=0 Wl,r−mid∗len>=0,也就是长度每多1,贡献都要减去 m i d mid mid,求最大值。这样子就不需要记录起点了。
- 显然建一个AC自动机,直接设状态 f [ i ] f[i] f[i]表示AC自动机上以 i i i为结尾的字符串最大的 W l , r − m i d ∗ l e n W_{l,r}-mid*len Wl,r−mid∗len。
- 既然是在AC自动机上DP,那么考虑下一个字符的节点是 x x x,那么考虑 f [ x ] f[x] f[x]的值,首先不难想到它直接从 f [ f a i l [ x ] ] f[fail[x]] f[fail[x]]转移过来,但是这样还有一段起点不会考虑到。
- 因此再设一个状态 g [ x ] g[x] g[x]表示从 f a i l [ x ] fail[x] fail[x]前为起点,到 x x x的最大值,那么 f [ x ] = m a x ( g [ x ] , f [ f a i l [ x ] ] ) f[x]=max(g[x],f[fail[x]]) f[x]=max(g[x],f[fail[x]])
- 考虑 g [ x ] g[x] g[x]的转移,设 y y y是 x x x的父亲,那么可以发现在 y y y跳fail的过程中,这些节点 f a i l [ y ] , f a i l [ f a i l [ y ] ] , f a i l [ . . . ] fail[y],fail[fail[y]],fail[...] fail[y],fail[fail[y]],fail[...]的 g g g所覆盖的起点区间刚好是不相交的,所以直接考虑将这些 g g g转移过来,再加上以当前位置为末尾的字符串的贡献 s u m [ f a i l [ x ] ] − m i d sum[fail[x]]-mid sum[fail[x]]−mid。
- 这样一直跳到一个 y y y使得 y y y有儿子 z z z和 x x x匹配,即 f a i l [ x ] = z fail[x]=z fail[x]=z
- 这就是一个跳fail的过程,并且所有加进去的 g g g都包括 f a i l [ x ] fail[x] fail[x],所以时间复杂度和正确性有了保证。
- 注意没有计算到整个串 x x x的贡献,单独补上。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 300005
#define ll long long
#define db double
using namespace std;
int n,q,m,p,i,j,k,x,y,z;
int id,tot,tr[maxn][26],fail[maxn],dep[maxn];
db f[maxn],g[maxn],sum[maxn],h[maxn];
int t,w,d[maxn],fa[maxn];
void prepare(){
for(i=0;i<26;i++) tr[0][i]=1;
t=0,w=1,d[1]=1;
while (t<w){
x=d[++t];
for(i=0;i<26;i++) if (tr[x][i]){
y=tr[x][i],dep[y]=dep[x]+1,d[++w]=y;
for(z=fail[x];!tr[z][i]&&z;z=fail[z]);
fail[y]=tr[z][i];
sum[y]+=sum[fail[y]],h[y]=h[x]+sum[y];
}
}
}
int check(db mid){
t=0,w=1,d[1]=1;
for(i=1;i<=tot;i++) f[i]=g[i]=-1e15;
while (t<w){
x=d[++t];
for(i=0;i<26;i++) if (tr[x][i]){
y=tr[x][i];
g[y]=max(g[y],g[x]-mid+sum[fail[y]]);
for(z=fail[x];!tr[z][i]&&z;z=fail[z])
g[y]=max(g[y],g[z]-mid+sum[fail[y]]);
g[y]=max(g[y],h[y]-mid*dep[y]);
f[y]=max(f[fail[y]],g[y]);
d[++w]=y;
}
}
db mx=-1e15;
for(x=id;x;x=fa[x])
mx=max(mx,f[x]);
return mx>=0;
}
int main(){
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
char ch=getchar();
tot=x=1;
while (ch>='a'&&ch<='z') {
if (!tr[x][ch-'a']) tr[x][ch-'a']=++tot;
fa[tr[x][ch-'a']]=x,x=tr[x][ch-'a'],ch=getchar();
} id=x;
scanf("%d",&m);
while (m--){
for(ch=getchar();ch<'a'||ch>'z';ch=getchar());
x=1;
while (ch>='a'&&ch<='z') {
if (!tr[x][ch-'a']) tr[x][ch-'a']=++tot;
fa[tr[x][ch-'a']]=x,x=tr[x][ch-'a'],ch=getchar();
}
scanf("%d",&k),sum[x]+=k;
}
prepare();
db L=0,R=1e10,mid,E=1e-6;
while (L+E<R){
mid=(L+R)/2;
if (check(mid)) L=mid;
else R=mid;
}
printf("%.4lf",R);
}