题面
题意
多组数据,每组数据给出一个长度为n的字符串S,求
max
i
∑
j
=
1
n
l
c
p
(
i
,
j
)
∗
k
j
\max_{i}{\sum_{j=1}^{n}lcp(i,j)*k_{j}}
maxi∑j=1nlcp(i,j)∗kj的最小值
其中
k
i
k_{i}
ki由你定,但是要满足
∑
i
=
1
n
k
i
=
1
\sum_{i=1}^{n}k_{i}=1
∑i=1nki=1,且
0
<
=
k
i
<
=
1
0<=k_{i}<=1
0<=ki<=1
做法
首先用后缀自动机对字符串的反串建出后缀树,然后考虑计算在后缀树中的每棵子树怎么分配k值。
对于一棵子树,如果根节点u是后缀节点,则很显然,如果这棵子树到的k值之和为x,那么根节点的答案就是
∑
l
c
p
(
i
,
u
)
∗
x
\sum{lcp(i,u)*x}
∑lcp(i,u)∗x,且
l
c
p
(
i
,
u
)
=
l
e
n
[
u
]
lcp(i,u)=len[u]
lcp(i,u)=len[u](先不考虑子树外的串的贡献),而且不难发现这是子树中所有串中最大的。
如果根节点u不是后缀节点,那么就要考虑将它的几个子节点的子树合并,我们已经递归求出了它的每棵子树的
k
k
k,表示如果分配给这棵子树
x
x
x,它的最小值将是
k
∗
x
k*x
k∗x,为了使所有子树的最大值最小,从那什均衡的角度思考,最好让它们都相等,如果分配给子树v,a的权值,那么它的贡献就是
a
∗
k
[
v
]
a*k[v]
a∗k[v](内部贡献)
+
(
1
−
a
)
∗
l
e
n
[
u
]
+(1-a)*len[u]
+(1−a)∗len[u](子树之间的贡献)
只要让几棵子树的这个值相等即可。
代码
#include<bits/stdc++.h>
#define db double
#define N 400100
using namespace std;
int T,n,tt,last;
char str[N];
db K[N];
struct Node
{
int to[26],len,pa;
bool bj;
}node[N<<1];
vector<int>to[N];
inline void add(int u)
{
int p=last,np=++tt;
last=np;
node[np].len=node[p].len+1;
for(;p&&!node[p].to[u];p=node[p].pa) node[p].to[u]=np;
node[np].bj=1;
if(!p)
{
node[np].pa=1;
return;
}
int q=node[p].to[u];
if(node[q].len==node[p].len+1)
{
node[np].pa=q;
return;
}
int nq=++tt;
node[nq].len=node[p].len+1;
node[nq].pa=node[q].pa;
memcpy(node[nq].to,node[q].to,sizeof(node[q].to));
node[np].pa=node[q].pa=nq;
for(;node[p].to[u]==q;p=node[p].pa) node[p].to[u]=nq;
}
void dfs(int now)
{
if(node[now].bj)
{
K[now]=node[now].len;
return;
}
int i,t,p;
db sum=0;
for(i=0;i<to[now].size();i++)
{
t=to[now][i];
dfs(t);
if(!i) sum+=1,p=t;
else sum+=(K[p]-node[now].len)/(K[t]-node[now].len);
}
sum=1.0/sum;
K[now]=(1-sum)*node[now].len+sum*K[p];
}
int main()
{
int i,j;
cin>>T;
while(T--)
{
for(i=1;i<=tt;i++)
{
to[i].clear();
node[i].pa=0;
node[i].len=0;
node[i].bj=0;
memset(node[i].to,0,sizeof(node[i].to));
}
tt=last=1;
scanf("%s",str+1);
n=strlen(str+1);
reverse(str+1,str+n+1);
for(i=1;i<=n;i++) add(str[i]-'a');
for(i=2;i<=tt;i++) to[node[i].pa].push_back(i);
dfs(1);
printf("%.10f\n",K[1]);
}
}