• 2018 焦作网络赛 L Poor God Water ( AC自动机构造矩阵、BM求线性递推、手动构造矩阵、矩阵快速幂 )


    题目链接

    题意 :

    实际上可以转化一下题意

    要求求出用三个不同元素的字符集例如 { 'A' 、'B' 、'C' }

    构造出长度为 n 且不包含

    AAA、BBB

    CCC、ACB

    BCA、CAC

    CBC

    这其中任意一个字符串的方案数

    分析 :

    方法一 (BM 求线性递推)

    直接暴力出前 10 项的答案、然后猜它其实可以由线性递推递推而来

    丢进杜教的 BM 模板里面就可以直接求出第 N 项了

    实际上这个可以不用猜、这种不包含某些串的题目

    如果你做过类似的、就会知道实际上是可以构造出一个矩阵然后快速幂

    可以使用矩阵快速幂优化的递推方程大概都可以用 BM 求

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    #include <string>
    #include <map>
    #include <set>
    #include <cassert>
    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i,a,n) for (int i=a;i<n;i++)
    #define per(i,a,n) for (int i=n-1;i>=a;i--)
    #define pb push_back
    #define mp make_pair
    #define all(x) (x).begin(),(x).end()
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    typedef vector<int> VI;
    typedef long long ll;
    typedef pair<int,int> PII;
    const ll mod=1000000007;
    ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
    // head
    
    ll n;
    namespace linear_seq {
        const int N=10010;
        ll res[N],base[N],_c[N],_md[N];
    
        vector<int> Md;
        void mul(ll *a,ll *b,int k) {
            rep(i,0,k+k) _c[i]=0;
            rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
            for (int i=k+k-1;i>=k;i--) if (_c[i])
                rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
            rep(i,0,k) a[i]=_c[i];
        }
        int solve(ll n,VI a,VI b) { // a 系数 b 初值 b[n+1]=a[0]*b[n]+...
            ll ans=0,pnt=0;
            int k=SZ(a);
            assert(SZ(a)==SZ(b));
            rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
            Md.clear();
            rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
            rep(i,0,k) res[i]=base[i]=0;
            res[0]=1;
            while ((1ll<<pnt)<=n) pnt++;
            for (int p=pnt;p>=0;p--) {
                mul(res,res,k);
                if ((n>>p)&1) {
                    for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
                    rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
                }
            }
            rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
            if (ans<0) ans+=mod;
            return ans;
        }
        VI BM(VI s) {
            VI C(1,1),B(1,1);
            int L=0,m=1,b=1;
            rep(n,0,SZ(s)) {
                ll d=0;
                rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
                if (d==0) ++m;
                else if (2*L<=n) {
                    VI T=C;
                    ll c=mod-d*powmod(b,mod-2)%mod;
                    while (SZ(C)<SZ(B)+m) C.pb(0);
                    rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                    L=n+1-L; B=T; b=d; m=1;
                } else {
                    ll c=mod-d*powmod(b,mod-2)%mod;
                    while (SZ(C)<SZ(B)+m) C.pb(0);
                    rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                    ++m;
                }
            }
            return C;
        }
        int gao(VI a,ll n) {
            VI c=BM(a);
            c.erase(c.begin());
            rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
            return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
        }
    };
    
    int main() {
        vector<int>v;
        v.push_back(3);
        v.push_back(9);
        v.push_back(20);
        v.push_back(46);
        v.push_back(106);
        v.push_back(244);
        v.push_back(560);
        v.push_back(1286);
        v.push_back(2956);
        v.push_back(6794);
        int nCase;
        scanf("%d", &nCase);
        while(nCase--){
            scanf("%lld", &n);
            printf("%lld
    ",1LL * linear_seq::gao(v,n-1) % mod);
        }
    }
    View Code

    方法二 (AC自动机构造矩阵 + 矩阵快速幂)

    如果你做过 POJ 2778 那么你就能够知道这个题目

    实际上将不合法的字符串丢到 AC 自动机里面去就可以构造出矩阵了

    然后进行快乐的矩阵快速幂就阔以了

    下面代码并非 AC 代码、只是使用 AC 自动机构造矩阵的代码

    且代码大部分都使用了我写过的 POJ 2778 的代码、输入形式可看代码最后注释

    #include<queue>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    
    const int Max_Tot = 1e4 + 10;
    const int Letter  = 3;
    const int MOD = 1e9+7;
    int maxn;
    int mp[256];
    
    struct mat{ int m[111][111]; }unit, M;
    
    mat operator * (mat a, mat b)
    {
        mat ret;
        long long x;
        for(int i=0; i<maxn; i++){
            for(int j=0; j<maxn; j++){
                x = 0;
                for(int k=0; k<maxn; k++){
                    x = (x + ((long long)a.m[i][k]%MOD * b.m[k][j]%MOD)%MOD)%MOD;
                }
                ret.m[i][j] = x % MOD;
            }
        }
        return ret;
    }
    
    inline void init_unit() { for(int i=0; i<maxn; i++) unit.m[i][i] = 1; }
    
    mat pow_mat(mat a, int n)
    {
        mat ret = unit;
        while(n){
            if(n&1) ret = ret * a;
            a = a*a;
            n >>= 1;
        }
        return ret;
    }
    
    struct Aho{
        struct StateTable{
            int Next[Letter];
            int fail, flag;
        }Node[Max_Tot];
        int Size;
        queue<int> que;
    
        inline void init(){
            while(!que.empty()) que.pop();
            memset(Node[0].Next, 0, sizeof(Node[0].Next));
            Node[0].fail = Node[0].flag = 0;
            Size = 1;
        }
    
        inline void insert(char *s){
            int now = 0;
            for(int i=0; s[i]; i++){
                int idx = mp[s[i]];
                if(!Node[now].Next[idx]){
                    memset(Node[Size].Next, 0, sizeof(Node[Size].Next));
                    Node[Size].fail = Node[Size].flag = 0;
                    Node[now].Next[idx] = Size++;
                }
                now = Node[now].Next[idx];
            }
            Node[now].flag = 1;
        }
    
        inline void BuildFail(){
            Node[0].fail = 0;
            que.push(0);
            while(!que.empty()){
                int top = que.front();  que.pop();
                if(Node[ Node[top].fail ].flag) Node[top].flag = 1;///如果当前节点的Fail指针指向的节点也是末尾节点,那么这个节点也是不合法的!
    
                for(int i=0; i<Letter; i++){
                    if(Node[top].Next[i]){
                        if(top == 0) Node[ Node[top].Next[i] ].fail = 0;
                        else{
                            int v = Node[top].fail;
                            while(v != -1){
                                if(Node[v].Next[i]){
                                    Node[ Node[top].Next[i] ].fail = Node[v].Next[i];
                                    break;
                                }v = Node[v].fail;
                            }if(v == -1) Node[ Node[top].Next[i] ].fail = 0;
                        }que.push(Node[top].Next[i]);
                    }else Node[top].Next[i] = top!=0?Node[ Node[top].fail ].Next[i]:0;///多了这一句!
                }
            }
        }
    
        inline void BuildMatrix(){
            for(int i=0; i<Size; i++)
                for(int j=0; j<Size; j++)
                    M.m[i][j] = 0;
            for(int i=0; i<Size; i++){
                for(int j=0; j<Letter; j++){
                    if(!Node[i].flag && !Node[ Node[i].Next[j] ].flag)
                        M.m[i][Node[i].Next[j]]++;
                }
            }
            maxn = Size;
        }
    
    }ac;
    
    char S[11];
    int main(void)
    {
        mp['A']=0,
        mp['B']=1,
        mp['C']=2;
        int n, m;
        while(~scanf("%d %d", &m, &n)){
            ac.init();
            for(int i=0; i<m; i++){
                scanf("%s", S);
                ac.insert(S);
            }
            ac.BuildFail();
            ac.BuildMatrix();
    
            for(int i=0; i<9; i++){
                for(int j=0; j<9; j++){
                    printf("%d ", M.m[i][j]);
                }puts("");
            }puts("");
    
            init_unit();
            M = pow_mat(M, n);
    
    //        for(int i=0; i<10; i++){
    //            for(int j=0; j<10; j++){
    //                printf("%d ", M.m[i][j]);
    //            }puts("");
    //        }puts("");
    
            int ans = 0;
            for(int i=0; i<ac.Size; i++)
                ans = (ans + M.m[0][i])%MOD;
            ans %= MOD;
            printf("%d
    ", ans);
        }
        return 0;
    }
    /*输入 7 和 n 然后输入字符集、就能出答案*/
    /**
    7 15
    AAA
    BBB
    CCC
    ACB
    BCA
    CAC
    CBC
    */
    View Code

    方法三 (手动构造矩阵 + 矩阵快速幂)

    首先使用各种手段抓一个聪明伶俐的队友过来

    此队友需要在矩阵构造方面颇有灵性

    把题目交给他翻译、构思、打代码

    等待 15 分钟左右、最后即可得到 Accept

  • 相关阅读:
    C#关机代码实例详解
    如何设计通用的网站模板
    C# XML解析方式实例解析1
    ASP.NET配置错误页面浅析
    几种常用的C#排序方法简介
    简述C# XML解析方法的特点及应用
    请不要相信
    浅谈ASP.NET Forms验证
    设计友好的错误信息页面
    详解.NET中容易混淆的委托与接口
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/9667894.html
Copyright © 2020-2023  润新知