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