Hdu 2243 考研路茫茫——单词情结 (字符串_DP(矩阵))

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2243


题目大意给定m个词根,现在要用26个字母组成长度小等于n的字符串并且至少含一个词根的组合种数,n <  2^31,结果对2^64次方取余


解题思路:好恶心的一道题,在我还习惯写代码行数为两位数的时候空降这一道代码200行的题目,做的那是一个蛋疼。最开始脑残把题目理解成只含一个词根的组合种数,矩阵想成Hdu 3962相似的矩阵,然后自己YY把答案补上然后与样例一样,结果就交了,果断Wa。一直未A,躺床上一想,是至少含一串词根,也就是总答案减去不含词根的种数,这样矩阵不就成了一个total * total的表示相互转移不会有词根出现的可达阵吗?然后就当作做一n次传递闭包,也就是matA^n,这是要求的一种,同理可知长度为n-1的为matA^(n-1),最后的结果是matA^1 + matA^2 + ..... + matA^n ,然后用26^n - 不含词根的种数就是答案了。这个可达阵就是从Trie图中得来,如果某个节点可以到另一个节点,就可达。

     本题对2^64取余特别恶心,必须用unsigned __int64(都是正数,上限比普通的__int64大一倍)或者unsigned long long。这题我一直TLE,一直搞不明白为什么,后来看了别人的解题报告,发现可以不用一直取膜,因为unsigned __int64越界了就变成0,相当于已经取模了,把各种取模去了之后就AC了。还有很多解题报告说n要用unsigned int,他们可能是求matA^(n+1),其实算到n就可以了。本题用到unsigned __int64,在用矩阵模版的时候会出现栈溢出,我用了最上面的那条命令后解解决了。


测试数据:

2 3
aa ab


1 2
a


1 2147483647
a


代码:

#pragma comment(linker, "/STACK:1024000000,1024000000") 
#include <stdio.h>
#include <string.h>
#define MAX 30
#define int64 unsigned __int64 

char dir[MAX];
int  m,total;
int64 n,mod,cnt;

struct node {

    int flag,in;
    node *fail,*next[26];
}*qu[MAX*MAX],arr[MAX*MAX],*root;

node *CreateNode() {

    node *p = &arr[total];
    p->in = total++;
    p->flag = 0;
    p->fail = NULL;
    for (int i = 0; i < 26; ++i)
        p->next[i] = NULL;
    return p;
}
void Insert(char *str) {

    int i = 0,k;
    node *p = root;


    while (str[i]) {

        if (p->flag) break;
        k = str[i++] - 'a';
        if (p->next[k] == NULL)
            p->next[k] = CreateNode();
        p = p->next[k];
    }
    p->flag = 1;
}
void Build_AC() {

    int head,tail,i;
    head = tail = 0;


    root->fail = root;
    qu[++head] = root;
    while (tail < head) {

        node *p = qu[++tail];
        for (i = 0; i < 26; ++i) {

            if (p->next[i] != NULL) {

                if (p == root) p->next[i]->fail = root;
                else p->next[i]->fail = p->fail->next[i];
                qu[++head] = p->next[i];
                p->next[i]->flag |= p->fail->next[i]->flag;
            }
            else {

                if (p == root) p->next[i] = root;
                else p->next[i] = p->fail->next[i];
            }//else
        }//for
    }//while
}//void


/*-----------Trie图构造完成-------------------*/
/*-----------    矩阵模板  -------------------*/


struct Mat{

    int64 mat[MAX][MAX],size;
    Mat(int n) {size = n,memset(mat,0,sizeof(mat));}


    friend Mat operator *(Mat a,Mat b);
    friend Mat operator +(Mat a,Mat b);
    friend Mat operator ^(Mat a,int k);
    friend Mat Sum(int k);
}A(MAX*2),E(MAX*2);
Mat operator *(Mat a,Mat b) {

    Mat c(total);
    for (int i = 0; i < total; ++i)
        for (int j = 0; j < total; ++j)
            for (int k = 0; k < total; ++k)
                if (a.mat[i][k] && b.mat[k][j])
                    c.mat[i][j] += a.mat[i][k] * b.mat[k][j];
    return c;
}
Mat operator +(Mat a,Mat b) {

    Mat c(total);
    for (int i = 0; i < total; ++i)
        for (int j = 0; j < total; ++j)
            c.mat[i][j] = a.mat[i][j] + b.mat[i][j];
    return c;
}
Mat operator ^(Mat a,int k) {

    Mat c = E;
    while (k) {

        if (k & 1) c = c * a;
        a = a * a,k >>= 1;
    }
    return c;
}
Mat Sum(int k) {

    if (k == 1) return A;
    else if (k & 1) return (A^k) + Sum(k-1);
    else return Sum(k/2) * ((A^(k/2)) + E);
}


/*-----------矩阵模板完成-------------------*/
/*-----------    开始DP  -------------------*/
void GetMat() {

    int i,j,k;
    A.size = E.size = total;
    memset(E.mat,0,sizeof(E.mat));
    memset(A.mat,0,sizeof(A.mat));


    for (i = 0; i < total; ++i) 
        if (arr[i].flag == 0){
            
            for (k = 0; k < 26; ++k) {
                
                j = arr[i].next[k]->in;    
                if (arr[i].next[k]->flag == 0)
                    A.mat[i][j]++;
            }
        }

    
    for (i = 0; i < E.size; ++i)
        E.mat[i][i] = 1;
}
int64 Mul(int n,int k) {

    int64 ans = 1;
    int64 m = n;
    while (k) {

        if (k & 1) ans = ans * m;
        m = m * m,k >>= 1;
    }
    return ans;
}
int64 IntSum(int n) {

    if (n == 1) return 26;
    else if (n & 1) return Mul(26,n) + IntSum(n - 1);
    else return IntSum(n/2) * ( Mul(26,n/2)  + 1);
}
int64 Solve_1A() {

    int i,j,k;

    int64 ans = IntSum(n);
    A = Sum(n);

    for (i = 0; i < total; ++i)
        ans = (ans - A.mat[0][i]) % mod;
    return ans;
}


int main()
{
    int i,j,k;
    for (i = mod = 1; i <= 64; ++i)
        mod = mod * 2;
    mod = mod - 1;


    while (scanf("%d%d",&m,&n) != EOF) {

        total = 0;
        root = CreateNode();
        for (i = 0; i < m; ++i)
            scanf("%s",dir),Insert(dir);
        
        
        Build_AC();
        GetMat();
        int64 ans = Solve_1A();
        printf("%I64u\n",(ans + mod) % mod);
    }
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值