hdu2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)

Posted fzuzyz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)相关的知识,希望对你有一定的参考价值。

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2243

题目大意:

  给出n个单词,问存在多少长度小于l的字符串,子串不含任意一个单词,答案对$2^{64}$取模。

  细节多如牛毛的一道题,可以看成是poj2778升级版,长度为定值l的做法,见https://www.cnblogs.com/FZUzyz/p/12885877.html,只是将字母数量从4个推广到了26个而已,没有别的变化。然后是这个坑爹模数,2的64次方,刚开始上了int128,因为两个乘起来的话long long绝对爆了,后面看了部分题解,发现直接开unsigned long long即可,因为ull就是64位,也不需要取模,自然溢出的结果就是他的答案,很巧妙有木有。

  随后的思路和poj2778一样,求出合法数量,总数量减去非法数量即可。先求总数量,对于长度小于l的,其数量应该为$sum_{i=1}^{l}26^{i}$,很明显是个等比数列求和公式对吧,是不是很心动,想用等比数列求和公式$sum=frac{26(26^{l}-1)}{25}$,没错,我就这么写的,然后样例都跑不出来,因为有除法,需要求逆元,而模数$2^{64}$又不是质数!所以另求他法,假设f(n)为长度为1-n的串数量总和,不难发现$f(n)=26f(n-1)+26$,这个式子很明显就可以用矩阵快速幂求了。那么总数量的问题就解决了。接下来求合法数量了,要求小于l的总和,显然是要求出$sum_{i=1}^{l}sum_{j=1}^{size}mat[0][j]$,mat为路径矩阵形式,size为矩阵大小。可以构造矩阵

egin{pmatrix}
mat & 1\\
0 & 1
end{pmatrix}

注意改造后的矩阵mat2大小为mat矩阵大小+1,将其右侧全部改为1,下方改为0(右下为1除外),这样一来,$mat2^{l}$就变成了

egin{pmatrix}
mat^{l} & sum_{i=1}^{l-1}sum_{j=1}^{size}mat[0][j]\\
0 & 1
end{pmatrix}

证明很容易,数学归纳法或者别的方法都可以证明。所以最后只需要将$mat2^{l}$第首行元素求和即可。

最后贴上AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <ll,ll> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define all(x) x.begin(),x.end()
#define pb push_back
#define fi first
#define se second
#define mes(a,b) memset(a,b,sizeof a)
#define mp make_pair
#define dd(x) cout<<#x<<"="<<x<<" "
#define de(x) cout<<#x<<"="<<x<<"
"
#define debug() cout<<"I love Miyamizu Mitsuha forever.
"
const int inf=0x3f3f3f3f;
const int maxn=105;


class matrix
{
    public:
        ull arrcy[35][35];
        int row,column;
        matrix()
        {
            memset(arrcy,0,sizeof arrcy);
            column=row=0;
        }
        friend matrix operator *(matrix s1,matrix s2)
        {
            int i,j;
            matrix s3;
            for (i=0;i<s1.row;i++)
            {
                for (j=0;j<s2.column;j++)
                {
                    for (int k=0;k<s1.column;k++)
                    {
                        s3.arrcy[i][j]+=s1.arrcy[i][k]*s2.arrcy[k][j];
                    }
                }
            }
            s3.row=s1.row;
            s3.column=s2.column;
            return s3;
        }
        void show()
        {
            for(int i=0;i<row;i++)
            {
                for (int j=0;j<column;j++)
                    cout<<arrcy[i][j]<<" ";
                cout<<endl;
            }
        }
}mat,mul;
matrix quick_pow(matrix s1,long long n)
{
    matrix mul=s1,ans;
    ans.row=ans.column=s1.row;
    memset(ans.arrcy,0,sizeof ans.arrcy);
    for(int i=0;i<ans.row;i++)
        ans.arrcy[i][i]=1;
    while(n)
    {
        if(n&1) ans=ans*mul;
        mul=mul*mul;
        n>>=1;
    }
    return ans;
}

ull qpow(ull a,ull b)
{
    ull ans=1;
    for(;b;b>>=1,a=a*a)
        if(b&1) ans*=a;
    return ans;
}
class Trie
{
    public:
        Trie()
        {
            cnt=1;
        }
        int cnt;
        int trie[maxn][26];
        int fail[maxn];
        bool bad[maxn];
        void init()
        {
            rep(i,0,cnt)
            {
                fail[i]=bad[i]=0;
                rep(j,0,26) trie[i][j]=0;
            }
            cnt=1;
        }
        void insert(string s)
        {
            int len=s.size();
            int pos=0;
            rep(i,0,len)
            {
                int next=s[i]-a;
                if(!trie[pos][next]) trie[pos][next]=cnt++;
                pos=trie[pos][next];
            }
            bad[pos]=1;
        }
        void getfail()
        {
            queue<int> q;
            rep(i,0,26)
            {
                if(trie[0][i])
                {
                    fail[trie[0][i]]=0;
                    q.push(trie[0][i]);
                }
            }
            while(!q.empty())
            {
                int pos=q.front();
                q.pop();
                rep(i,0,26)
                {
                    bad[pos]|=bad[fail[pos]];
                    if(trie[pos][i])
                    {
                        fail[trie[pos][i]]=trie[fail[pos]][i];
                        q.push(trie[pos][i]);
                    }
                    else trie[pos][i]=trie[fail[pos]][i];
                }
            }
        }
}ac;
string s;
ull cal(ll x)
{
    matrix l,r;
    r.row=2;r.column=1;
    r.arrcy[0][0]=0;
    r.arrcy[1][0]=1;
    l.row=l.column=2;
    l.arrcy[0][0]=l.arrcy[0][1]=26;
    l.arrcy[1][0]=0;l.arrcy[1][1]=1;
    r=quick_pow(l,x)*r;
    return r.arrcy[0][0];
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,l;
    while(cin>>n>>l)
    {
        ac.init();
        mes(mul.arrcy,0);mes(mat.arrcy,0);
        rep(i,0,n)
        {
            cin>>s;
            ac.insert(s);
        }
        ac.getfail();
        mat.row=1;
        mat.column=ac.cnt+1;
        mat.arrcy[0][0]=1;
        mul.row=mul.column=ac.cnt+1;
        rep(i,0,ac.cnt)
        {
            if(ac.bad[i]) continue;
            rep(j,0,26)
            {
                if(!ac.bad[ac.trie[i][j]]) mul.arrcy[i][ac.trie[i][j]]++;
            }
        }
        rep(i,0,mul.column) mul.arrcy[i][mul.column-1]=1;
        mat=mat*quick_pow(mul,l);
        ull ans=0;
        rep(i,0,mat.column) ans=(ans+mat.arrcy[0][i]);
        ans--;
        ull cnt=cal(l);
        cnt-=ans;
        cout<<cnt<<"
";
    }
    return 0;
}

 

以上是关于hdu2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)的主要内容,如果未能解决你的问题,请参考以下文章

HDU2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)

hdu2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)

hdu2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)

HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)

HDU 2243 考研路茫茫――单词情结 ——(AC自动机+矩阵快速幂)

HDU 2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)