HDU 6405 Make ZYB Happy(广义SAM)

It's known to all that ZYB is godlike, so obviously he has a large number of titles, such as jskingjsking, bijingzybbijingzyb and nbazybnbazyb. ZYB likes his titles very much.

Each of ZYB's titles is a string consisting of lower case letters 'a'-'z''a'-'z' associated with a happiness value hihi, which shows how much ZYB likes this title. If you say any substring of some title with happiness value xx, he will get xx happiness points. Moreover, a string may appear in more than one title. In this case, the happiness points ZYB gets are multiplied. If the string you say is not the substring of any of his titles, he gets no happiness point.



For example, let's say ZYB has two titles: zybnbzybnb (with happiness value 3) and ybybybyb (with happiness value 5). If you say yy, bb or ybyb, ZYB will get 15 happiness points; if you say zz, zyzy or zybzyb, ZYB will only get 3 happiness points; if you say ybzybz or ybacybac he will get 0 happiness points.

One day, you find ZYB pretty sad. As a big fan of ZYB, you want to say a word to ZYB to cheer him up. However, ZYB is really busy, so you can only say no more than mm letters. As you haven't seen ZYB for a long time, you are so excited that you forget what you want to say, so you decide to choose to say a nonempty string no longer than mm and only containing 'a'-'z''a'-'z' with equal probability. You want to know the expectations of happiness points you will bring to ZYB for different mm.

InputThe first line contains an integer n(1n104)(1≤n≤104), the number of titles ZYB has.

The ii-th of the next nn lines contains a nonempty string titi, which only contains lower case letters 'a'-'z''a'-'z', representing the ii-th title. The sum of lengths of all titles does not exceed 3×1053×105.

Then follows a line with nn integers hihi (1hi106)(1≤hi≤106), the happiness value of ii-th title.

The next line is a single integer Q(1Q3×105)(1≤Q≤3×105), the number of queries.

For the next QQ lines, each contains a single integer m(1m106)(1≤m≤106), meaning that you can say no more than mm letters to ZYB.

The input data contains only one test case.
OutputFor each query, display a single line of integer, representing the answer. It can be proved that the answer can be uniquely written as p/qp/q where pp and qq are non-negative integers with gcd(p,q)=gcd(q,109+7)=1gcd(p,q)=gcd(q,109+7)=1, and you should display pq1mod(109+7)p⋅q−1mod(109+7), where q1q−1 means the multiplicative inverse of qq modulo 109+7109+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;    
} 
View Code

 



posted @ 2019-10-09 15:36  StarHai  阅读(584)  评论(0编辑  收藏  举报