http://acm.hdu.edu.cn/showproblem.php?pid=2243

这是一题AC自动机 + 矩阵快速幂的题目,

首先知道总答案应该是26^1 + 26^2 + 26^3 .... + 26^L,用等比数列的前n项和是无法做的,因为出现小数。

这个可以直接看到F[n] = 26 * F[n - 1] + 26,然后矩阵快速幂即可。

然后需要减去那些一个词根都不包含的单词的总数,这个可以用AC自动机算出来。就是至少包含一个词根的答案。

现在关键就是求,长度小于等于L的通路总数。

我们知道通路等于L的通路总数,正是一个可达矩阵e[i][j]的L次幂。

那么小于等于L的,也就是e + e^2 + e^3 + e^4 + ... + e^L

这是无法求的。

做法是新增一个节点,然后每一个节点都和这个新的节点连上一条边。

假设本来的可达矩阵是

e[1][1] = 1, e[1][2] = 1, e[1][3] = 1

e[2][1] = 0, e[2][2] = 0, e[2][3] = 1;

e[3][1] = 0, e[3][2] = 0, e[3][3] = 0;

那么长度是2的通路数就是,e^2,然后得到了这个图。

所以长度是2的通路数,是4。e[1][1] ---> e[1][1],e[1][1]--->e[1][2]。e[1][1] ---> e[1][3],e[1][2]--->e[2][3]

那么长度是1的通路数就不能统计了,就是e[1][1]、e[1][2]、e[1][3]丢失了。

那么我们新增一个节点,使得其他本来的节点都和它连一条边。

然后算e^2的时候,

就会把e[1][1]记录到e[1][1]-->e[1][4]中,所以记录成功。、

然后e[1][4]本来是不存在的,所以这条路径是多余的,

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL; #include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
typedef unsigned long long int ULL;
const int N = ;
struct node {
int flag;
int id;
struct node *Fail; //失败指针,匹配失败,跳去最大前后缀
struct node *pNext[N];
} tree[ * ];
int t; //字典树的节点
struct node *create() { //其实也只是清空数据而已,多case有用
struct node *p = &tree[t++];
p->flag = ;
p->Fail = NULL;
p->id = t - ;
for (int i = ; i < N; i++) {
p->pNext[i] = NULL;
}
return p;
}
void toinsert(struct node **T, char str[]) {
struct node *p = *T;
if (p == NULL) {
p = *T = create();
}
for (int i = ; str[i]; i++) {
int id = str[i] - 'a';
if (p->pNext[id] == NULL) {
p->pNext[id] = create();
}
p = p->pNext[id];
}
p->flag++; //相同的单词算两次
return ;
}
void BuiltFail(struct node **T) {
//根节点没有失败指针,所以都是需要特判的
//思路就是去到爸爸的失败指针那里,找东西匹配,这样是最优的
struct node *p = *T; //用个p去代替修改
struct node *root = *T;
if (p == NULL) return ;
//树上bfs,要更改的是p->pNext[i]->Fail
struct node *que[t + ]; //这里的t是节点总数,字典树那里统计的,要用G++编译
int head = , tail = ;
que[tail++] = root;
while (head < tail) {
p = que[head]; //p取出第一个元素 ★
for (int i = ; i < N; i++) { //看看存不存在这个节点
if (p->pNext[i] != NULL) { //存在的才需要管失败指针。
if (p == root) { //如果爸爸是根节点的话
p->pNext[i]->Fail = root; //指向根节点
} else {
struct node *FailNode = p->Fail; //首先找到爸爸的失败指针
while (FailNode != NULL) {
if (FailNode->pNext[i] != NULL) { //存在
p->pNext[i]->Fail = FailNode->pNext[i];
if (FailNode->pNext[i]->flag) {
p->pNext[i]->flag = ;
}
break;
}
FailNode = FailNode->Fail; //回溯
}
if (FailNode == NULL) { //如果还是空,那么就指向根算了
p->pNext[i]->Fail = root;
}
}
que[tail++] = p->pNext[i]; //这个id是存在的,入队bfs
} else if (p == root) { //变化问题,使得不存在的边也建立起来。
p->pNext[i] = root;
} else {
p->pNext[i] = p->Fail->pNext[i]; //变化到LCP。可以快速匹配到病毒。
}
}
head++;
}
return ;
} const int maxn = + ;
struct Matrix {
ULL a[maxn][maxn];
int row;
int col;
};
//应对稀疏矩阵,更快。
struct Matrix matrix_mul(struct Matrix a, struct Matrix b) { //求解矩阵a*b%MOD
struct Matrix c = {}; //这个要多次用到,栈分配问题,maxn不能开太大,
//LL的时候更加是,空间是maxn*maxn的,这样时间用得很多,4和5相差300ms
c.row = a.row; //行等于第一个矩阵的行
c.col = b.col; //列等于第二个矩阵的列
for (int i = ; i <= a.row; ++i) {
for (int k = ; k <= a.col; ++k) {
if (a.a[i][k]) { //应付稀疏矩阵,0就不用枚举下面了
for (int j = ; j <= b.col; ++j) {
c.a[i][j] += a.a[i][k] * b.a[k][j];
}
}
}
}
return c;
}
struct Matrix quick_matrix_pow(struct Matrix ans, struct Matrix base, int n) {
//求解a*b^n%MOD
while (n) {
if (n & ) {
ans = matrix_mul(ans, base);//传数组不能乱传,不满足交换律
}
n >>= ;
base = matrix_mul(base, base);
}
return ans;
} int n, L;
char str[];
void work() {
t = ;
struct node *T = NULL;
for (int i = ; i <= n; ++i) {
scanf("%s", str + );
toinsert(&T, str);
}
BuiltFail(&T);
t--;
Matrix e = {};
e.row = e.col = t + ;
for (int i = ; i <= t; ++i) {
if (tree[i].flag) continue;
int id1 = tree[i].id;
for (int j = ; j < N; ++j) {
if (tree[i].pNext[j]->flag) continue;
int id2 = tree[i].pNext[j]->id;
e.a[id1][id2]++;
}
}
t++;
for (int i = ; i <= t; ++i) {
e.a[i][t] = ;
}
Matrix I = {};
I.row = I.col = t;
for (int i = ; i <= t; ++i) {
I.a[i][i] = ;
}
Matrix res = quick_matrix_pow(I, e, L); I.row = , I.col = ;
I.a[][] = , I.a[][] = ;
e.row = e.col = ;
e.a[][] = , e.a[][] = ;
e.a[][] = , e.a[][] = ;
Matrix res2 = quick_matrix_pow(I, e, L);
ULL ans = res2.a[][];
// cout << ans << endl;
for (int i = ; i <= t; ++i) {
ans -= res.a[][i];
}
ans++; //减了一个多余的路径
cout << ans << endl;
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
while (scanf("%d%d", &n, &L) > ) work();
return ;
}

HDU 2243 考研路茫茫——单词情结 求长度小于等于L的通路总数的方法的更多相关文章

  1. hdu 2243 考研路茫茫——单词情结(AC自动+矩阵)

    考研路茫茫——单词情结 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  2. HDU 2243 考研路茫茫——单词情结

    考研路茫茫——单词情结 Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HDU. Original ID ...

  3. HDU 2243 考研路茫茫——单词情结(AC自动机+矩阵)

    考研路茫茫——单词情结 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  4. hdu 2243 考研路茫茫——单词情结 AC自动机 矩阵幂次求和

    题目链接 题意 给定\(N\)个词根,每个长度不超过\(5\). 问长度不超过\(L(L\lt 2^{31})\),只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个? 思路 状态(AC自动 ...

  5. HDU 2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)

    http://acm.hdu.edu.cn/showproblem.php?pid=2243 题意: 给出m个模式串,求长度不超过n的且至少包含一个模式串的字符串个数. 思路: 如果做过poj2778 ...

  6. HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)

    背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了. 一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如"ab",放在单词前一般 ...

  7. Hdu 2243 考研路茫茫——单词情结 (AC自己主动机+矩阵)

    哎哟喂.中文题. . .不说题意了. 首先做过POJ 2778能够知道AC自己主动机是能够求出长度为L的串中不含病毒串的数量的. POJ 2778的大概思路就是先用全部给的病毒串建一个AC自己主动机. ...

  8. hdu 2243 考研路茫茫——单词情结 ac自动机+矩阵快速幂

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=2243 题意:给定N(1<= N < 6)个长度不超过5的词根,问长度不超过L(L <23 ...

  9. HDU 2243 考研路茫茫——单词情结 ( Trie图 && DP && 矩阵构造幂和 )

    题意 :  长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里就不考虑单词是否有实际意义. 比如一共有2个词根 aa 和 ab ,则可能存在104个长度不超过3的单词, ...

随机推荐

  1. CodeForces - 1005E2:Median on Segments (General Case Edition) (函数的思想)

    You are given an integer sequence a1,a2,…,ana1,a2,…,an. Find the number of pairs of indices (l,r)(l, ...

  2. 1131 Subway Map(30 分)

    In the big cities, the subway systems always look so complex to the visitors. To give you some sense ...

  3. JZOJ 1667【AHOI2009】中国象棋——dp

    题目:https://jzoj.net/senior/#main/show/1667 只注重0.1.2的列有多少个,不注重它们的位置,就能记录了. #include<iostream> # ...

  4. centos7 install python3

    1. 过程 # 1. root权限, 安装依赖 yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-dev ...

  5. 按钮交互loading ---- 转圈圈 加载

    按钮loading状态自定义选项(功能): 可以在元素上添加 data-am-loading 来设置选项: spinner 加载动画图标,适用于支持 CSS3 动画.非 input 元素,写图标名称即 ...

  6. linux下使用c判断文件夹是否为空的小程序

    自己写了一个 判断文件夹是否为空的小代码 //文件夹操作相关的函数的帮助$: man 3 readdir #include <stdio.h> #include <sys/types ...

  7. Web.config文件中关于Cookie安全性的考量和设置

    cookie的内容,如图所示: HTTP response header: Set-Cookie: <name>=<value>[; <Max-Age>=<a ...

  8. ADT-Bundle--Android开发环境快速搭建

    http://blog.csdn.net/aizquan/article/details/8974750

  9. Coreseek 安装问题

    Ubuntu下安装coreseek mmseg出现了cannot find input file: src/Makefile.in 解决方法如下 >autoheader >automake ...

  10. 关于$_SERVER['PHP_SELF']用法及其安全性---改良

    网站来源:http://www.5idev.com/p-php_server_php_self.shtml PHP 使用 $_SERVER['PHP_SELF'] 获取当前页面地址及其安全性问题 PH ...