题目链接: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原创,但可以转载,因为我们是兄弟。