题目链接
好的吧,我并不知道纳什均衡是什么
反正遥遥领先跟我说这题姑且可以把纳什均衡理解成每个子树分配的比例乘以权值都相等
查询的是lcp
那么就先把后缀树建出来,然后每个后缀就是np类节点,每两个np节点之间的lca的len就是lcp
考虑树型DP
dp[i]表示第i个点的所有子树平分1产生的最小值
这个点是np类节点,那么显然这个点直接赋成他的len返回就可以了,因为肯定给这个点直接赋1是最优的
不是的话,就考虑纳什均衡,假设第一个子树分配到a1,第二个分配到a2……
每个点的贡献是
k
[
i
]
∗
a
i
+
(
1
−
a
i
)
l
e
n
k[i]*a_i+(1-a_i)len
k[i]∗ai+(1−ai)len
其中len为lca的长度
拆一下式子贡献为
l
e
n
+
a
i
∗
(
k
[
i
]
−
l
e
n
)
len+ai*(k[i]-len)
len+ai∗(k[i]−len)然后所有的子树这个贡献都要相等
那么显然根据
(
k
[
i
]
−
l
e
n
)
(k[i]-len)
(k[i]−len)的比例分配
a
i
a_i
ai就可以了
分配完之后再根据最上面那个公式还原该点的贡献
代码如下:
#include<bits/stdc++.h>
#define N 200010
using namespace std;
int n;
struct SAM
{
char s[N];
struct point
{
int fa,len,son[26];
}t[N<<1];
int cnt=1,last=1,sz[N<<1];
double k[N<<1];
vector<int> g[N<<1];
int add(int c)
{
int p=last;
int np=++cnt;
t[np].len=t[p].len+1;
sz[np]=1;
while(p&&!t[p].son[c])
{
t[p].son[c]=np;
p=t[p].fa;
}
if(!p) t[np].fa=1;
else
{
int q=t[p].son[c],nq;
if(t[p].len+1==t[q].len)
{
t[np].fa=q;
}
else
{
nq=++cnt;
t[nq]=t[q];
t[nq].len=t[p].len+1;
t[np].fa=t[q].fa=nq;
while(p&&(t[p].son[c]==q))
{
t[p].son[c]=nq;
p=t[p].fa;
}
}
}
last=np;
}
int dfs(int now)
{
if(sz[now])
{
k[now]=t[now].len;
return 0;
}
double sum=0.0;
int tmp;
for(int i=0;i<g[now].size();i++)
{
dfs(g[now][i]);
if(i==0) sum+=1,tmp=g[now][i];
else sum+=(k[tmp]-t[now].len)/(k[g[now][i]]-t[now].len);
}
sum=1.0/sum;
k[now]=(1-sum)*t[now].len+sum*k[tmp];
}
void init()
{
for(int i=1;i<=cnt;i++) g[i].clear(),memset(t[i].son,0,sizeof(t[i].son)),k[i]=0,sz[i]=0;
last=1;
cnt=1;
}
void solve()
{
scanf("%s",s);
n=strlen(s);
reverse(s,s+n);
for(int i=0;i<n;i++) add(s[i]-'a');
for(int i=1;i<=cnt;i++) g[t[i].fa].push_back(i);
dfs(1);
printf("%.12lf\n",k[1]);
}
}sam;
int ttt;
int main()
{
scanf("%d",&ttt);
while(ttt--) sam.init(),sam.solve();
}