原題:2018 ICPC Asia-East Continent Final Jios
想看原題解的能夠去看吉老師的直播題解ide
(dllca膜你賽搬原題差評)spa
考慮題目中給出的式子的含義,實際上至關於要給串$s$的每一個後綴分配一個機率$p_i$知足$\sum\limits_{i=1}^{n}p_i=1$,而後取其中一個與其它後綴的lcp指望值最小的後綴,要作的就是求出一種最優的分配p的方案使得最後的最小值最大;code
先不考慮後綴,考慮若干個lcp爲0(即沒有公共前綴)的字符串如何分配最優:blog
設有$m$個串$s_1,s_2,...,s_m$,長度分別爲$l_1,l_2,...,l_m$,那麼取其中一個串$s_t$對答案的貢獻是$p_tl_t$,最後的答案就是$min\{p_tl_t\}$;字符串
一個結論:要使答案最大,全部$p_tl_t$的值一定相等;get
若是不全相等,答案就是其中最小的那個,則必然能夠經過調整$p$使得最小的那個增長一點,其餘的所有減少一點,從而使答案更優;直播
因而能夠列出一個方程組:string
$$\begin{cases}\sum\limits_{i=1}^{n}p_i=1 \\ p_1l_1=p_2l_2=\cdots=p_nl_n=k\end{cases}$$it
其中$k$就是答案,聯立兩式得:
$$k=\frac{1}{\frac{1}{l_1}+\frac{1}{l_2}+\cdots+\frac{1}{l_n}}$$
顯然就能夠直接求出$k$了;
回到原問題,涉及到快速求後綴的lcp容易想到先構造出後綴樹,因爲後綴樹本質上仍是一棵trie樹,所以一個節點全部的後繼節點以及向下的子樹所表示的字符串在這個點向後的部分都是沒有公共前綴的,因此能夠用上面的方法來處理;
注意到其實某一個節點子樹中的問題是整個後綴樹上問題徹底等價的子問題,所以能夠在後綴樹上dfs,父節點直接加上子樹的答案繼續合併便可;
dfs的時候注意若是一個節點自己已是原串某一個後綴的尾節點那麼它的後繼節點確定沒有貢獻,直接返回0便可;
因而就作完了,講了這麼多代碼仍是很短的!
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #include<queue>
7 #define inf 2147483647
8 #define eps 1e-9
9 using namespace std; 10 typedef long long ll; 11 typedef double db; 12 struct edge{ 13 int v,next; 14 }a[1000001]; 15 int t,n,tot,last,rt,tote,head[1000001],son[1000001][26],mx[1000001],fa[1000001],isp[1000001]; 16 char s[200001]; 17 void add(int u,int v){ 18 a[++tote].v=v; 19 a[tote].next=head[u]; 20 head[u]=tote; 21 } 22 void extend(int ch){ 23 int p=last,np=++tot; 24 mx[np]=mx[p]+1; 25 for(;p&&!son[p][ch];p=fa[p])son[p][ch]=np; 26 if(!p)fa[np]=rt; 27 else{ 28 int q=son[p][ch]; 29 if(mx[q]==mx[p]+1)fa[np]=q; 30 else{ 31 int nq=++tot; 32 mx[nq]=mx[p]+1; 33 memcpy(son[nq],son[q],sizeof(son[q])); 34 fa[nq]=fa[q]; 35 fa[q]=fa[np]=nq; 36 for(;p&&son[p][ch]==q;p=fa[p])son[p][ch]=nq; 37 } 38 } 39 isp[np]=true; 40 last=np; 41 } 42 db dfs(int u){ 43 if(isp[u])return 0; 44 db ret=0; 45 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){ 46 int v=a[tmp].v; 47 ret+=1.0/(dfs(v)+mx[v]-mx[u]); 48 } 49 return 1.0/ret; 50 } 51 int main(){ 52 memset(head,-1,sizeof(head)); 53 scanf("%d",&t); 54 while(t--){ 55 scanf("%s",s); 56 n=strlen(s); 57 rt=last=++tot; 58 for(int i=n-1;i>=0;i--){ 59 extend(s[i]-'a'); 60 } 61 for(int i=rt+1;i<=tot;i++){ 62 add(fa[i],i); 63 } 64 printf("%.10lf\n",dfs(rt)); 65 } 66 return 0; 67 }