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

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 = 26;
struct node {int flag;int id;struct node *Fail;    //失败指针,匹配失败,跳去最大前后缀struct node *pNext[N];
} tree[100 * 20];
int t;     //字典树的节点
struct node *create() {   //其实也只是清空数据而已,多case有用struct node *p = &tree[t++];p->flag = 0;p->Fail = NULL;p->id = t - 1;for (int i = 0; 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 = 1; 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]->Failstruct node *que[t + 20]; //这里的t是节点总数,字典树那里统计的,要用G++编译int head = 0, tail = 0;que[tail++] = root;while (head < tail) {p = que[head]; //p取出第一个元素 ★for (int i = 0; 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 = 1;}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 = 30 + 3;
struct Matrix {ULL a[maxn][maxn];int row;int col;
};
//应对稀疏矩阵,更快。
struct Matrix matrix_mul(struct Matrix a, struct Matrix b) { //求解矩阵a*b%MODstruct Matrix c = {0};  //这个要多次用到,栈分配问题,maxn不能开太大,//LL的时候更加是,空间是maxn*maxn的,这样时间用得很多,4和5相差300msc.row = a.row; //行等于第一个矩阵的行c.col = b.col; //列等于第二个矩阵的列for (int i = 1; i <= a.row; ++i) {for (int k = 1; k <= a.col; ++k) {if (a.a[i][k]) { //应付稀疏矩阵,0就不用枚举下面了for (int j = 1; 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%MODwhile (n) {if (n & 1) {ans = matrix_mul(ans, base);//传数组不能乱传,不满足交换律
        }n >>= 1;base = matrix_mul(base, base);}return ans;
}int n, L;
char str[222];
void work() {t = 1;struct node *T = NULL;for (int i = 1; i <= n; ++i) {scanf("%s", str + 1);toinsert(&T, str);}BuiltFail(&T);t--;Matrix e = {0};e.row = e.col = t + 1;for (int i = 1; i <= t; ++i) {if (tree[i].flag) continue;int id1 = tree[i].id;for (int j = 0; 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 = 1; i <= t; ++i) {e.a[i][t] = 1;}Matrix I = {0};I.row = I.col = t;for (int i = 1; i <= t; ++i) {I.a[i][i] = 1;}Matrix res = quick_matrix_pow(I, e, L);I.row = 1, I.col = 2;I.a[1][1] = 0, I.a[1][2] = 1;e.row = e.col = 2;e.a[1][1] = 26, e.a[1][2] = 0;e.a[2][1] = 26, e.a[2][2] = 1;Matrix res2 = quick_matrix_pow(I, e, L);ULL ans = res2.a[1][1];
//    cout << ans << endl;for (int i = 1; i <= t; ++i) {ans -= res.a[1][i];}ans++; //减了一个多余的路径cout << ans << endl;
}int main() {
#ifdef localfreopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endifwhile (scanf("%d%d", &n, &L) > 0) work();return 0;
}
View Code

 

转载于:https://www.cnblogs.com/liuweimingcprogram/p/6490034.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/269905.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为什么需要StringBuffer

(1)为什么需要StringBuffer 由于String的内容是不可变的&#xff0c;在频繁操作字符串的应用中&#xff0c;导致String对象泛滥&#xff0c;不断的被创建和销毁&#xff0c;占用大量的内存和CPU时间。 例如&#xff0c;将十万个"A"连成一个大的字符串。代码如下: 这…

猿创征文|2022个人开发工具集积累和分享

目录 1、谷歌浏览器 2、华为浏览器 3、腾讯文档 3、Notepad 4、IntelliJ IDEA 5、VisualStudio 6、VSCode 7、微信开发者工具 8、Navicat 9、PDManer 10、Git/Tortoise Git 11、Axure 12、NxShell 13、ApiPost 14、Xmind 15、Everything 16、WizTree 17、猿如意 今天给大家分享…

在idea里如何实现Git项目回滚

在idea里如何实现Git项目回滚 先我们要回滚的项目右键&#xff0c;如下&#xff1a; 点击Git,再点击Show History 选择你要回滚的分支 然后右键&#xff0c;如下&#xff1a; 点击Copy Revision Number 点击项目&#xff0c;右键&#xff0c;Git -------> Repository -…

人工智能:TensorFlow深度学习框架介绍

目录 1、TensorFlow简介 2、TensorFlow的主要任务 3. TensorFlow的特点 4、TensorFLow的缺点 5、TensorFlow的用途 6、实际案例 6.1 自动驾驶 6.2 安卓手机自拍功能 6.3 智能音箱 6.4智能医疗 今天给大家简单介绍一下TensorFlow深度学习框架&#xff0c;欢迎互相交流学习&#…

idea启动java服务报错OutOfMemoryError: GC overhead limit exceeded解决方法

在用idea开发java项目时&#xff0c;启动报内存溢出错误&#xff0c;致服务启动失败&#xff1a; Error:java: java.lang.OutOfMemoryError: GC overhead limit exceeded 报此错说明启动期间内存不够用了&#xff0c;把idea的启动进程堆内存值设大点就行了。 设置窗口&#x…

QQ微信刷屏助手 FlashScreenAssist 1.2发布

主要功能 文字刷屏图片刷屏简易教程 文字刷屏 打开软件之后输入要刷屏的文字&#xff0c;点击【开始】&#xff0c;然后点一下微信或者QQ的输入框&#xff0c;就会开始刷屏了&#xff0c;注意【时间间隔】不要调太小&#xff0c;越小越快。 要注意的是。必须在英文输入法下面才…

电脑技巧:Win10自带的6个实用功能,你都会用吗

目录 1、系统自带计算器 2、Win10自带截图功能 3、 Win10自带的手写输入 4、历史剪贴板 5、定时提醒 6、自带录屏功能 1、系统自带计算器 系统里自带了一个计算器大家都知道&#xff0c;打开是这个样子。 但当你点击计算器左上角的三个横杠图标后会发现这个计算器功能非常强大。…

Java多线程系列——深入重入锁ReentrantLock

简述 ReentrantLock 是一个可重入的互斥&#xff08;/独占&#xff09;锁&#xff0c;又称为“独占锁”。 ReentrantLock通过自定义队列同步器&#xff08;AQS-AbstractQueuedSychronized&#xff0c;是实现锁的关键&#xff09;来实现锁的获取与释放。 其可以完全替代 synchro…

前端:nodejs版本管理神器nvm软件使用笔记

目录 1、nvm简介 2、nvm的作用 3、nvm下载安装 4、整理常用的nvm命令 使用vue框架开发的朋友可能会遇到首次运行公司项目环境的时候&#xff0c;会出现使用npm install命令安装依赖包的时候出现各种各样的问题&#xff0c;其中很重要的一个错误原因就是因为你的nodejs版本和当时…

NetCore实战:基于html生成pdf文件案例讲解

目录 一、WkHtmlToPdfDotNet介绍 二、安装 三、运行效果 一、WkHtmlToPdfDotNet介绍 WkHtmlToPdfDotNet是基于本地 wkhtmltopdf封装的.NET Core类库&#xff0c;主要通过webkit引擎实现html页面转换为pdf文件。并且支持在Windows、Docker、Linux、MacOSX运行。 主要功能实现在线…

struts2+spring3+hibernate3+mysql简单登录实现

1.导入相关的jar包 2.建立数据库 1 create table account( 2 id int(10), 3 user varchar(50), 4 paw varchar(50) 5 ); 6 insert into account values(1,admin,admin); 3.建立包结构 4.配置文件的配置及代码 4.1 数据库配置文件&#xff1a;db.properties 1 #jdbc 2 jdbc.driv…

Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法

Spring 事务机制回顾 Spring事务一个被讹传很广说法是&#xff1a;一个事务方法不应该调用另一个事务方法&#xff0c;否则将产生两个事务. 结果造成开发人员在设计事务方法时束手束脚&#xff0c;生怕一不小心就踩到地雷。 其实这是不认识Spring事务传播机制而造成的误解&…

java多线程一览

线程概述:多线程的目的,不是提高程序的执行速度,而是提高程序的使用率(能抢到CPU的可能比较大). 因为线程是CPU调度的基本单位,所以,当一个程序的线程较多的时候就更容易抢到cpu的资源进程: 运行中的程序,是系统进行资源分配和调度的独立单位每个进程都有他自己的内存空间和系统…

人工智能:PyTorch深度学习框架介绍

目录 1、PyTorch 2、PyTorch常用的工具包 3、PyTorch特点 4、PyTorch不足之处 今天给大家讲解一下PyTorch深度学习框架的一些基础知识&#xff0c;希望对大家理解PyTorch有一定的帮助&#xff01; 1、PyTorch PyTorch是一个基于Torch的Python机器学习框架。它是由Facebook的人工…

Linux下安装配置MySQL

一、删除原来的MySQL 在安装前要先确定系统是否已经安装了其他版本的MySQL&#xff0c;如已安装其他版本的MySQL&#xff0c;需先删除后再安装新版本。 1. 执行yum命令&#xff0c;删除MySQL的lib库&#xff0c;服务文件 yum remove mysql mysql-server mysql-libs mysql-serve…