HDU 6405 2018HDU多校赛 第八场 Make ZYB Happy(广义后缀自动机)

Make ZYB Happy

Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 21    Accepted Submission(s): 13


 

Problem Description

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

Each of ZYB's titles is a string consisting of lower case letters 'a'-'z' associated with a happiness value hi , which shows how much ZYB likes this title. If you say any substring of some title with happiness value x , he will get x 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: zybnb (with happiness value 3) and ybyb (with happiness value 5). If you say y , b or yb , ZYB will get 15 happiness points; if you say z , zy or zyb , ZYB will only get 3 happiness points; if you say ybz or ybac 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 m 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 m and only containing 'a'-'z' with equal probability. You want to know the expectations of happiness points you will bring to ZYB for different m .  

Input

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

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

Then follows a line with n integers hi (1≤hi≤106) , the happiness value of i -th title.

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

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

The input data contains only one test case.

 

Output

For each query, display a single line of integer, representing the answer. It can be proved that the answer can be uniquely written as p/q where p and q are non-negative integers with gcd(p,q)=gcd(q,109+7)=1 , and you should display p⋅q−1mod(109+7) , where q−1 means the multiplicative inverse of q modulo 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.  

Source

2018 Multi-University Training Contest 8


 

大致题意:给你n个字符串,然后每个字符串有一个快乐值。然后给你m个询问,每个询问给你一个长度,让你写出一个不大于这个长度的字串。这个字串的权值定义为,如果这个字符串中出现过第i个给定字符串的子串,那么权值乘以第i个字符串的快乐值,最后答案就是多个快乐值相乘。现在问你给定长度的字符串权值的期望。

首先,我们考虑这个权值的分母是多少。显然长度不超过x,那就是说长度可以是1、2、...、x,对应26个字母,那么方案数就是26^1+26^2+...+26^x。然后我们再来看分母。分母肯定是计算每一个长度的贡献,对于一个长度,他可以是很多个串的子串,所以我们考虑每一个给定字符串的贡献,而每一个字符串的贡献取决于它对应的所有自动机上面的节点。

由于需要用到很多个字符串的所有子串,所以很自然而然的想到了用广义后缀自动机这个数据结构。我们考虑首先把所有的字符串添加到广义后缀自动机里面,然后对于每一个串,我们直接把每一个串放到后缀自动机上面跑,每次遍历到一个点,对应一个子串,我们统计节点以及它的parent,也就是这个子串的所有后缀的贡献。当前串对上述节点的贡献就是当前串对应的快乐值,上述节点的贡献乘以这个快乐值即可。当然了,对于同一个串,对每个节点的贡献只会有一次,所有要保证每个点只计算一次贡献。

之后,我们再按照长度来计算贡献。对于每一个节点,我已经统计了其对应子串的贡献,但是我们发现,如果节点x对应的串出现了,产生的贡献是y,那么T[x].fa~x之间所有的点都会出现,产生的贡献同样也是。为什么会这样呢?还是得从后缀自动机parent指针的定义出发。parent指针所指就是某一个串x的最长后缀,根据right数组的定义,parent的出现次数要比x多,那么长度介于最长后缀和本身长度之间的后缀的出现次数肯定与x的出现次数相同。如果不同,那么parent肯定会指向第一个不相同的后缀对应的节点。所以说这从parent的长度加一到x的长度,这一整段的贡献我们都要计算上去。我们用一个前缀和数组sum,记录对应长度的贡献。对应的,区间 [T[T[x].fa].len+1,T[x].len] 上的贡献都是y,表现在sum上面就是两个端点一加一减。统计完毕后,对sum求一遍前缀和,之后sum[i]表示所有长度为i的串的贡献。然后再次对sum求一次前缀和,这样的话sum[i]就表示所有长度为1~i的串的贡献。

计算出分子sum[i]之后,我们再求逆元乘以分母的逆元就是最后的结果了。总的来说,就是不断的建立和遍历后缀自动机,所以总的时间复杂度就是O(N)的。具体见代码:

#include<bits/stdc++.h>
#define LL long long
#define mod 1000000007
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define sf(x) scanf("%d",&x)
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define clr(x,n) memset(x,0,sizeof(x[0])*(n+5))
#define IO ios::sync_with_stdio(0);cin.tie(0); cout.tie(0)
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)

using namespace std;

const int N = 1e6+10;

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

int pw[N],h[N],flag[N],ans[N],sum[N],n,m;
string s[N]; bool vis[N];

struct Suffix_Automation
{
    int tot,cur,n,c[N],sa[N],right[N];
    struct node{int ch[26],len,fa;} T[N];
    void init(){cur=tot=1;memset(T,0,sizeof(T));}

    void ins(int x,int id)
    {
        int p=cur;cur=++tot;T[cur].len=id;right[cur]++;
        for(;p&&!T[p].ch[x];p=T[p].fa) T[p].ch[x]=cur;
        if (!p) {T[cur].fa=1;return;}int q=T[p].ch[x];
        if (T[p].len+1==T[q].len) {T[cur].fa=q;return;}
        int np=++tot; memcpy(T[np].ch,T[q].ch,sizeof(T[q].ch));
        T[np].fa=T[q].fa; T[q].fa=T[cur].fa=np; T[np].len=T[p].len+1;
        for(;p&&T[p].ch[x]==q;p=T[p].fa) T[p].ch[x]=np;
    }

    void cal(string s,int val,int tag)
    {
        int cur=1;
        for(int i=0;s[i];i++)
        {
            cur=T[cur].ch[s[i]-'a'];
            for(int tmp=cur;tmp&&flag[tmp]!=tag;tmp=T[tmp].fa)
                ans[tmp]=1LL*ans[tmp]*val%mod,flag[tmp]=tag;
        }
    }

    void build(int cur)
    {
        vis[cur]=1;
        sum[T[T[cur].fa].len+1]=(sum[T[T[cur].fa].len+1]+ans[cur])%mod;
        sum[T[cur].len+1]=(sum[T[cur].len+1]-ans[cur]+mod)%mod;
        for(int i=0;i<26;i++)
        {
            int nxt=T[cur].ch[i];
            if (nxt&&!vis[nxt]) build(nxt);
        }
    }

} SAM;

void init()
{
    pw[0]=1;
    for(int i=1;i<N;i++)
        pw[i]=(LL)pw[i-1]*26LL%mod;
    for(int i=2;i<N;i++)
        pw[i]=(pw[i]+pw[i-1])%mod;
}

int main()
{
    IO;
    SAM.init();
    init(); cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i]; SAM.cur=1;
        for(int j=0;s[i][j];j++)
            SAM.ins(s[i][j]-'a',j+1);
    }
    fill(ans,ans+N,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<N;i++)
        sum[i]=(sum[i]+sum[i-1])%mod;
    for(int i=1;i<N;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;
}

 

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/81712614
今日推荐