Description
Solution
没想到。。真没想到,以为是退火或者消元的
后缀数组的板子都不记得了
两个lcp实际上就是区间height的最小值,我们每次取出最小值的位置考虑
有一个很重要的结论是对于某一段,最优答案与分配的k值之和是成正比的,也就是各k值之间是成固定比例的。关于这一点我并不会证明,希望能有大爷告诉我啥的。。
也就是说我们并不需要知道具体某一段分配了多少,只需要给左区间分配x,那么右区间分配1-x,最后算出来的答案各自乘上分配的系数即可
设左区间答案为a,右区间答案为b,左区间分配x,我们实际上要求
max
(
a
x
+
(
1
−
x
)
h
,
(
1
−
x
)
b
+
x
h
)
\max(ax+(1-x)h,(1-x)b+xh)
max(ax+(1−x)h,(1−x)b+xh)
注意到两边都是单调的,因此最大值取在它们相等的时候
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
const int N=2000005;
char str[N];
int sa[N],rank[N],s2[N],r1[N],ct[N],h[N];
int mn[21][N],n;
void pre() {
fill(ct,0);
rep(i,1,n) ct[rank[i]=str[i]-'a'+1]++;
rep(i,1,26) ct[i]+=ct[i-1];
drp(i,n,1) sa[ct[rank[i]]--]=i;
for (int j=1,k=0,mx=26;k<n;j<<=1,mx=k) {
int p=0;
rep(i,n-j+1,n) s2[++p]=i;
rep(i,1,n) if (sa[i]>j) s2[++p]=sa[i]-j;
fill(ct,0);
rep(i,1,n) ct[rank[s2[i]]]++;
rep(i,1,mx) ct[i]+=ct[i-1];
drp(i,n,1) sa[ct[rank[s2[i]]]--]=s2[i];
r1[sa[1]]=k=1;
rep(i,2,n) {
if (rank[sa[i-1]]==rank[sa[i]]&&rank[sa[i-1]+j]==rank[sa[i]+j]) {
r1[sa[i]]=k;
} else r1[sa[i]]=++k;
}
rep(i,1,n) rank[i]=r1[i];
}
h[1]=0;
for (int i=1,j=0;i<=n;++i) {
if (rank[i]==1) continue;
j=std:: max(h[rank[i-1]]-1,0);
while (str[i+j]==str[sa[rank[i]-1]+j]) j++;
h[rank[i]]=j;
}
rep(i,1,n) mn[0][i]=i;
rep(j,1,20) {
int len=1<<j-1;
rep(i,1,n-len+1) {
if (h[mn[j-1][i]]<=h[mn[j-1][i+len]]) {
mn[j][i]=mn[j-1][i];
} else mn[j][i]=mn[j-1][i+len];
}
}
}
int query(int l,int r) {
// l=rank[l],r=rank[r];
// if (l>r) std:: swap(l,r);
l++;
int lg=log2(r-l+1);
if (h[mn[lg][l]]<=h[mn[lg][r-(1<<lg)+1]]) return mn[lg][l];
return mn[lg][r-(1<<lg)+1];
}
double solve(int l,int r) {
if (l==r) return n-sa[l]+1;
int mid=query(l,r);
double a=solve(l,mid-1),b=solve(mid,r);
double x=(b-h[mid])/(a+b-2*h[mid]);
return a*x+h[mid]*(1-x);
}
int main(void) {
freopen("second.in","r",stdin);
freopen("second.out","w",stdout);
scanf("%s",str+1);
n=strlen(str+1);
pre();
double ans=solve(1,n);
printf("%.6lf\n", ans);
return 0;
}