1. 程式人生 > 實用技巧 >CCPC2020長春 G題 Monkey's Keyboard

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;
}