CCPC2020长春 G题 Monkey's Keyboard
链接:
https://codeforces.com/gym/102832/problem/G
Pro:
给定一个
Sol:
很容易想到另一个题,叫歌唱王国。
然后按照那道题的套路推一发式子
发现对于任意一个字符串的
\[\begin{align*}
ans_S&=\sum_{i,i\ is\ a\ border\ of\ S} \prod_{j=1}^i \frac{1}{p_i}
\\
&=\sum_{i,i\ is\ a\ border\ of\ S} w_i (写成一个前缀积的形式)
\end{align*}
\]
发现这个式子的贡献
只和border有关
然后总答案的话可以考虑枚举border是谁
然后算这个border的贡献是多少
具体地说
首先,枚举border显然可以用SAM来实现
然后考虑贡献怎么计算
简单推到即可发现
对于SAM上任意一个节点\(x\)
其贡献为
\[\begin{align*}
ans&=\sum_{len=a}^b\sum_{r=1}^{cnt[x]} \sum_{l=1}^r \frac{w_{pos}}{w_{pos-len}}
\\
&=\frac{cnt_x*(cnt_x+1)}{2}*\sum_{len=a}^b\frac{w_{pos}}{w_{pos-len}}
\\
&=\frac{cnt_x*(cnt_x+1)}{2}*{w_{pos}}*\sum_{len=a}^b\frac{1}{w_{pos-len}}
\end{align*}
\]
对于w数组的倒数搞一个前缀和即可
#include<bits/stdc++.h>
#define N 1100000
#define db double
#define ll long long
#define ldb long double
#define ull unsigned long long
using namespace std;
const int h=3,ki=149,mo=1e9+7;
int mod(int x){return (x%mo+mo)%mo;}
int inc(int x,int k){x+=k;return x<mo?x:x-mo;}
int dec(int x,int k){x-=k;return x>=0?x:x+mo;}
int ksm(int x,int k)
{
int ans=1;
while(k){if(k&1)ans=1ll*ans*x%mo;k>>=1;x=1ll*x*x%mo;}
return mod(ans);
}
int inv(int x){return ksm(x,mo-2);}
int read()
{
char ch=0;int x=0,flag=1;
while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0',ch=getchar();}
return x*flag;
}
void write(int x)
{
if(!x)return (void)putchar(48);
if(x<0)putchar(45),x=-x;
int len=0,p[20];
while(x)p[++len]=x%10,x/=10;
for(int i=len;i>=1;i--)putchar(p[i]+48);
}
const db eps=1e-7,inf=1e9+7,pi=acos(-1);
db Read(){db x;scanf("%lf",&x);return x;}
void Write(db x){printf("%lf",x);}
char ch[N];
int root=1,size=1,last=1,sz[N];
struct node{int pos,len,lnk,nxt[26];}s[N];
void insert(int k)
{
int cur=++size,p=last;last=cur;
sz[cur]=1;s[cur].pos=s[cur].len=s[p].len+1;
while(p&&!s[p].nxt[k])s[p].nxt[k]=cur,p=s[p].lnk;
if(!p){s[cur].lnk=root;return;}
int q=s[p].nxt[k];
if(s[p].len+1==s[q].len)s[cur].lnk=q;
else
{
int clone=++size;
s[clone]=s[q];s[clone].len=s[p].len+1;
while(p&&s[p].nxt[k]==q)s[p].nxt[k]=clone,p=s[p].lnk;
s[q].lnk=s[cur].lnk=clone;
}
}
struct edge{int to,nxt;}e[N];
int num,head[N];
void add(int x,int y){e[++num]={y,head[x]};head[x]=num;}
void dfs(int x,int fa)
{
for(int i=head[x];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(to==fa)continue;
dfs(to,x);sz[x]+=sz[to];
}
}
int p[N],w[N],v[N];
int main()
{
scanf("%s",ch+1);
int n=strlen(ch+1),tot=0,ans=0;
for(int i=1;i<=n;i++)insert(ch[i]-'a');
for(int i=0;i<26;i++)p[i]=read(),tot=inc(tot,p[i]);
for(int i=0;i<26;i++)p[i]=1ll*tot*inv(p[i])%mo;
w[0]=1;for(int i=1;i<=n;i++)w[i]=1ll*w[i-1]*p[ch[i]-'a']%mo;
v[0]=1;for(int i=1;i<=n;i++)v[i]=inc(v[i-1],inv(w[i]));
num=-1;memset(head,-1,sizeof(head));
for(int x=2;x<=size;x++)add(s[x].lnk,x);dfs(1,1);
for(int x=2;x<=size;x++)
{
int a=s[s[x].lnk].len+1,b=s[x].len;
int l=s[x].pos-b,r=s[x].pos-a;
int t=1ll*(sz[x]+1)*sz[x]%mo*inv(2)%mo*w[s[x].pos]%mo;
if(r>=0)ans=inc(ans,1ll*t*v[r]%mo);
if(l-1>=0)ans=dec(ans,1ll*t*v[l-1]%mo);
}
write(ans);
return 0;
}