HDU 2243 ( Trie图 矩阵构造幂和 )

题意 :  长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里就不考虑单词是否有实际意义。

比如一共有2个词根 aa 和 ab ,则可能存在104个长度不超过3的单词,分别为
(2个) aa,ab, 
(26个)aaa,aab,aac...aaz, 
(26个)aba,abb,abc...abz, 
(25个)baa,caa,daa...zaa, 
(25个)bab,cab,dab...zab。

分析: 我们可以用Tire图跑矩阵快速幂的方法,去求长度为n不包含给定单词的词为sum; 所以想到求:长度为n包含给定单词的词 的算法就是用总的方案数-长度为n不包含给定单词的词的方案数为26^n-sum;  这题的难点是求长度不超过L的方案数,就是说我们需要求 26-sum1+26^2-sum2+26^3-sum3......26^n-sumn = (26+26^2+...26^n)-(sum1+sum2+...sumn); 我们显然不是遍历求; 考虑优先算法:

假设原 Trie 图构建出来的状态矩阵为 A ,那么同样的我们需要构造一个幂和即 A1 + A2 + A+ ..... + A然后最后的答案便是 ∑AL(0, i)  ( i ∈ 1~矩阵长度 ) ,那怎么去构造这两个幂和呢?

只要利用这个公式即可,用原矩阵 + 单位矩阵 + 零矩阵构造出新矩阵,最后右上角的矩阵便是幂和的矩阵

需要注意的点:(1)在求(26+26^2+...26^n)的时候不能用等比数列公式去求,这样会有误差,可以用上面构造的矩阵的方法构造

| 26,1 |

|0 , 1 |  的跑矩阵快速幂

(2 对于 2^64次方求模) 直接开unsigned long long 就好

#include<string.h>
#include<stdio.h>
#include<iostream>
#include<queue>
#define ULL unsigned long long
using namespace std;

const int Max_Tot = 1e2 + 10;
const int Letter  = 26;
int maxn;///矩阵的大小
char S[11];

struct mat{ ULL m[111][111]; }unit, M;
mat operator * (mat a, mat b){
    mat ret;
    for(int i=0; i<maxn; i++){
        for(int j=0; j<maxn; j++){
            ret.m[i][j] = (ULL)0;
            for(int k=0; k<maxn; k++){
                ret.m[i][j] += a.m[i][k]*b.m[k][j];
            }
        }
    }
    return ret;
}

inline void init_unit() {
    for(int i=0; i<maxn; i++)
        unit.m[i][i] = 1;
}

mat pow_mat(mat a, long long 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 = s[i] - ‘a‘;
            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 = -1;
        for(int i=0; i<Letter; i++){
            if(Node[0].Next[i]){
                Node[Node[0].Next[i]].fail = 0;
                que.push(Node[0].Next[i]);
            }else Node[0].Next[i] = 0;///必定指向根节点
        }
        while(!que.empty()){
            int top = que.front(); que.pop();
            if(Node[Node[top].fail].flag) Node[top].flag = 1;
            for(int i=0; i<Letter; i++){
                int &v = Node[top].Next[i];
                if(v){
                    que.push(v);
                    Node[v].fail = Node[Node[top].fail].Next[i];
                }else v = Node[Node[top].fail].Next[i];
            }
        }
    }

    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;

ULL GetSum(long long num){
    mat ret;
    ret.m[0][0] = 26;
    ret.m[0][1] = 1;
    ret.m[1][0] = 0;
    ret.m[1][1] = 1;
    int tmp = maxn;
    maxn = 2;
    ret = pow_mat(ret, ++num);
    maxn = tmp;
    return ret.m[0][1]-1;
}

ULL GetElimination(long long num){
    mat tmp;
    for(int i=0; i<maxn; i++)///左上角 为 原矩阵
        for(int j=0; j<maxn; j++)
            tmp.m[i][j] = M.m[i][j];

    for(int i=0; i<maxn; i++)///右上角 为 单位矩阵
        for(int j=maxn; j<(maxn<<1); j++)
            tmp.m[i][j] = (i+maxn == j);

    for(int i=maxn; i<(maxn<<1); i++)///左下角 为 零矩阵
        for(int j=0; j<maxn; j++)
            tmp.m[i][j] = 0;

    for(int i=maxn; i<(maxn<<1); i++)///右下角 为 单位矩阵
        for(int j=maxn; j<(maxn<<1); j++)
            tmp.m[i][j] = (i==j);

    int Temp = maxn;
    maxn <<= 1;///先将原本矩阵的大小放大一倍进行快速幂运算,这个和我快速幂的写法有关
    tmp = pow_mat(tmp, ++num);
    ULL ret = (ULL)0;
    maxn = Temp;///再回复成原来大小
    for(int i=maxn; i<(maxn<<1); i++)///右上角的矩阵就是幂和了
        ret += tmp.m[0][i];

    return (--ret);///需要 -1
}

int main(void)
{
    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();
        init_unit();
        ULL Tot = GetSum((long long)n);///注意是传long long不然会爆int
        ULL Elimination = GetElimination((long long)n);
        cout<<Tot-Elimination<<endl;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/shuaihui520/p/11615477.html

时间: 2024-10-10 14:35:26

HDU 2243 ( Trie图 矩阵构造幂和 )的相关文章

hdu 2243 AC自动机 + 矩阵快速幂

// hdu 2243 AC自动机 + 矩阵快速幂 // // 题目大意: // // 给你一些短串,问在长度不超过k的任意串,包含至少一个这些短串的其中 // 一个.问这样的串有多少个. // // 解题思路: // // 首先, 包含和不包含是一种互斥关系,包含+不包含 = 全集u.全集的答案就是 // 26 ^ 1 + 26 ^ 2 + .... + 26 ^ k.不包含的比较好求.构建一个自动机,得到 // 一个转移矩阵A.表示状态i能到状态j的方法数.而这些状态中都是不包含所给的 //

DNA Sequence POJ - 2778 邻接矩阵 trie图 矩阵快速幂

首先构造trie图. 我们明确一点的是,给出trie图,那么所有点的转移方式都是唯一可以确定的.即使是没有这个字符,他也会指向根节点. 我们根据离散数学的知识可以知道.计算有向图的邻接矩阵,然后k次方,就能够计算出从某一个点到另一个点,有多少条长度为k的路径. 故,我们构造出来trie图,拿出该图的邻接矩阵,就能计算路径数目.--(注意改图是有向图)-- trie图的构造不说了,模板. 邻接矩阵的构造根据trie图来的.我们在trie图上找到每一个节点,查看他的相邻节点,即A,G,C,T四个点指

POJ - 2778 ~ HDU - 2243 AC自动机+矩阵快速幂

这两题属于AC自动机的第二种套路通过矩阵快速幂求方案数. 题意:给m个病毒字符串,问长度为n的DNA片段有多少种没有包含病毒串的. 根据AC自动机的tire图,我们可以获得一个可达矩阵. 关于这题的tire图详解可以点击这里,往下面翻,这个博主的图对于tire图讲的非常详细. 知道了什么是tire图,理解了tire图后,后面的AC自动机的题目才能写. AC自动机的灵魂应该就是tire图 然后问题就变成了,得到了一个可达矩阵后,如何求方案数呢? 这个n = 2000000000 这咋办呢? 给定一

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

考研路茫茫--单词情结 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6853    Accepted Submission(s): 2383 Problem Description 背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了.一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如

HDU 2604 Queuing (矩阵快速幂)

HDU 2604 Queuing (矩阵快速幂) ACM 题目地址:HDU 2604 Queuing 题意: n个人排队,f表示女,m表示男,包含子串'fmf'和'fff'的序列为O队列,否则为E队列,有多少个序列为E队列. 分析: 矩阵快速幂入门题. 下面引用巨巨解释: 用f(n)表示n个人满足条件的结果,那么如果最后一个人是m的话,那么前n-1个满足条件即可,就是f(n-1): 如果最后一个是f那么这个还无法推出结果,那么往前再考虑一位:那么后三位可能是:mmf, fmf, mff, fff

HDU 2604 Queuing,矩阵快速幂

题目地址:HDU 2604 Queuing 题意: 略 分析: 易推出:   f(n)=f(n-1)+f(n-3)+f(n-4) 构造一个矩阵: 然后直接上板子: /* f[i] = f[i-1] + f[i-3] + f[i-4] */ #include<cstdio> #include<cstring> using namespace std; const int N = 4; int L, M; struct mtx { int x[N+1][N+1]; mtx(){ mem

HDU 2254 奥运(矩阵快速幂+二分等比序列求和)

HDU 2254 奥运(矩阵快速幂+二分等比序列求和) ACM 题目地址:HDU 2254 奥运 题意: 中问题不解释. 分析: 根据floyd的算法,矩阵的k次方表示这个矩阵走了k步. 所以k天后就算矩阵的k次方. 这样就变成:初始矩阵的^[t1,t2]这个区间内的v[v1][v2]的和. 所以就是二分等比序列求和上场的时候了. 跟HDU 1588 Gauss Fibonacci的算法一样. 代码: /* * Author: illuz <iilluzen[at]gmail.com> * B

POJ 2778 DNA Sequence(AC自动机确定DFA转移图+矩阵快速幂)

这道题极好的展示了AC自动机在构造转移图DFA上的应用 DFA转移图就是展示状态的转移过程的图,DFA图构造出来后就可以用DP求出任何DNA长度下,任何状态的个数 本题用自动机求出DFA矩阵,那么有 | dp[n][0] dp[n][1] ... dp[n][m] |=|dp[1][0] dp[1][1] ... dp[1][m] | * DFA^(n-1)    (m指状态总数) DP边界矩阵|dp[1][0] dp[1][1] ... dp[1][m] | 也就是DFA的第一行,所以dp[n

HDU 2842 Chinese Rings(矩阵快速幂+递推)

题目地址:HDU 2842 这个游戏是一个九连环的游戏. 假设当前要卸下前n个环.由于要满足前n-2个都卸下,所以要先把前n-2个卸下,需要f(n-2)次.然后把第n个卸下需要1次,然后这时候要卸下第n-1个,然后此时前n-2个都已经被卸下了.这时候把前n-2个都卸下与都装上所需的次数是一样的,因为卸下与装上的规则是一样的.所以又需要f(n-2)次,这时候前n-1个都在上面,卸下前n-1个需要f(n-1)次. 所以,总共需要2*f(n-2)+f(n-1)+1次. 然后构造如下矩阵. 1,2,1