InputThe first line contains an integer 109+7.Sample Input
2 zybnb ybyb 3 5 4 1 2 3 4
Sample Output
769230776
425925929
891125950
633120399
Hint
For the first query, you can bring him 3 happiness points if you say "z" or "n", and 15 happiness points if you say "y" or "b"; all other strings of length 1 bring no happiness point to ZYB. Therefore, the expectation is (2×3+2×15)/26 = 18/13, and the answer is 18×13^(-1) mod (10^9+7) = 769230776.
题意:
给你n个字符串,然后每个字符串有一个快乐值。然后给你m个询问,每个询问给你一个长度,让你写出一个不大于这个长度的字串。这个字串的权值定义为,如果这个字符串中出现过第i个给定字符串的子串,那么权值乘以第i个字符串的快乐值,最后答案就是多个快乐值相乘。现在问你给定长度的字符串权值的期望。
思路:
由于要用到多个字符串的所有子串,所以我们很容易想到广义SAM.对于一个长度m,那么它的贡献为长度1~m所有子串的贡献和,考虑它的分母就是26,262,263...26m 的和,我们可以预处理出来。考虑它的分子就是后缀自动机上面所有出现的长度小于等于m的子串的贡献和。在后缀自动机中的parent树中,如果p所代表的子串出现的话,那么fa[p]所代表的的子串一定出现,那么len[fa[p]]+1~len[p]的长度都会出现,而且贡献为len[p]的贡献,根据right数组的定义,parent的出现次数要比x多,那么长度介于最长后缀和本身长度之间的后缀的出现次数肯定与x的出现次数相同。如果不同,那么parent肯定会指向第一个不相同的后缀对应的节点。所以说这从parent的长度加一到x的长度,这一整段的贡献我们都要计算上去。我们用一个前缀和数组sum,记录对应长度的贡献。对应的,区间 [len[fa[x]]+1,len[x]] 上的贡献都是y,表现在sum上面就是两个端点一加一减。统计完毕后,对sum求一遍前缀和,之后sum[i]表示所有长度为i的串的贡献。然后再次对sum求一次前缀和,这样的话sum[i]就表示所有长度为1~i的串的贡献。
参考代码:
#include<bits/stdc++.h> using namespace std; #define mod 1000000007 typedef long long ll; const int maxn=1e6+10; int pw[maxn],n,m; int flag[maxn],h[maxn],sum[maxn],ans[maxn]; string s[maxn]; bool vis[maxn]; int qpow(int a,int b) { int ans=1; while(b) { if(b&1)ans=(ll)ans*a%mod; a=(ll)a*a%mod; b>>=1; } return ans; } void Init() { pw[0]=1; for(int i=1;i<maxn;i++) pw[i]=(ll)pw[i-1]*26ll%mod; for(int i=2;i<maxn;i++) pw[i]=(pw[i]+pw[i-1])%mod; } struct SAM{ int fa[maxn<<1],l[maxn<<1],nxt[maxn<<1][26]; int last,tot; void Init() { last=tot=1; memset(nxt[tot],0,sizeof nxt[tot]); l[tot]=fa[tot]=0; } int NewNode() { ++tot; memset(nxt[tot],0,sizeof nxt[tot]); l[tot]=fa[tot]=0; return tot; } void Insert(int ch) { int p,q,np,nq; if(nxt[last][ch]) { p=last;q=nxt[p][ch]; if(l[q]==l[p]+1) last=q;////// else { nq=NewNode(); l[nq]=l[p]+1;fa[nq]=fa[q]; memcpy(nxt[nq],nxt[q],sizeof(nxt[q])); fa[q]=nq; while(p&&nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p]; last=nq;////// } } else { np=NewNode(),p=last; last=np; l[np]=l[p]+1; while(p&&!nxt[p][ch]) nxt[p][ch]=np,p=fa[p]; if(!p) fa[np]=1; else { q=nxt[p][ch]; if(l[q]==l[p]+1) fa[np]=q; else { nq=NewNode(); memcpy(nxt[nq],nxt[q],sizeof nxt[q]); fa[nq]=fa[q]; l[nq]=l[p]+1; fa[q]=fa[np]=nq; while(p&&nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p]; } } } } void cal(string s,int val,int tag) { int cur=1; for(int i=0;s[i];i++) { cur=nxt[cur][s[i]-'a']; for(int tmp=cur;tmp&&flag[tmp]!=tag;tmp=fa[tmp]) ans[tmp]=1LL*ans[tmp]*val%mod,flag[tmp]=tag; } } void build(int cur) { vis[cur]=1; sum[l[fa[cur]]+1]=(sum[l[fa[cur]]+1]+ans[cur])%mod; sum[l[cur]+1]=(sum[l[cur]+1]-ans[cur]+mod)%mod; for(int i=0;i<26;i++) { int Nxt=nxt[cur][i]; if(Nxt&&!vis[Nxt]) build(Nxt); } } } sam; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); Init(); sam.Init(); cin>>n; for(int i=1;i<=n;++i) { cin>>s[i]; sam.last=1; for(int j=0;s[i][j];++j) sam.Insert(s[i][j]-'a'); } fill(ans,ans+maxn,1); for(int i=1;i<=n;++i) cin>>h[i]; for(int i=1;i<=n;++i) sam.cal(s[i],h[i],i); sam.build(1); sum[0]=0; for(int i=1;i<maxn;++i) sum[i]=(sum[i]+sum[i-1])%mod; for(int i=1;i<maxn;++i) sum[i]=(sum[i]+sum[i-1])%mod; cin>>m; while(m--) { int k; cin>>k; cout<<1LL*sum[k]*qpow(pw[k],mod-2)%mod<<endl; } return 0; }