\(\mathscr{Description}\)

  Private link.

  令 \(\Sigma=\{\texttt a,\texttt b\}\),对于所有形式语言 \(L\subseteq\Sigma^n\),\(L\) 的最小 DFA 状态数的最大值,以及取到这一最大值时,\(|L|\) 的最小值和最大值。

  \(n\le10^3\)。

\(\mathscr{Solution}\)

  引理 对于任意串 \(x,y\in\Sigma^\star\),定义 \(x\equiv_Ly\),当且仅当 \(\forall z\in\Sigma^\star,xz\in L\equiv yz\in L\)。语言 \(L\) 的最小 DFA 状态数就是等价关系 \(\equiv_L\) 在 \(\Sigma^\star\) 上划分出的等价类数目。

  直接考虑 \(L\) 的最小 DFA 的形态,可以发现:

  • 对于任意状态,从开始状态转移到它的路径长度唯一;
  • 不存在两个状态的转移完全相同(输入同样字符,转移向同一个状态)。

  根据性质一,我们将 DFA 的 DAG 分层,那么总状态数就是每层的状态数之和。令 \(Q_i~(i=0,1,\dots,m)\) 为第 \(i\) 层的状态,则 \(Q_0=\{q_s\}\) 为开始状态,\(Q_m=\{q_t\}\) 为唯一接受状态,此时对于第 \(k\) 层,从 \(Q_{k-1}\) 对 \(Q_k\) 的限制来看,显然 \(|Q_k|\le2|Q_{k-1}|\);从 \(Q_{k+1}\) 对 \(Q_k\) 的限制来看,为了满足性质二,\(Q_{k+1}\) 最多为 \(Q_k\) 内的某个点提供 \((|Q_{k+1}|+1)^2-1\) 种转移的选择(\(1\) 表示空状态,即分别枚举 \(\texttt a\) 和 \(\texttt b\) 转移向谁,不能同时为空)。根据这一构造,每个 \(|Q_k|\) 的上界显然可以同时取到,所以第一问就解决了。

  设 \(Q_p\) 为最后一层满足 \(Q_p=2|Q_{p-1}|\) 的状态,此后,\(Q_k\) 的大小都将由性质二的限制决定。前后都已经固定,\(|L|\) 只取决于 \(\delta_p:Q_p\times\Sigma\rightarrow Q_{p+1}\) 的长相。

  先来刻画 \(Q_{p+1}\) 内每个点对应的后缀串数量。令 \(G_k(x)~(k=p+1,\dots,m)\) 表示 \(Q_k\) 中,状态数量关于状态对应后缀串数量的 GF,那么 \(G_k(x)=(G_{k+1}(x)+1)^2-1\),其实就是 \(G_k(x)=(x+1)^{2^{m-k}}-1\)。

  然后,讨论一下 \(|Q_p|\) 和 \(|Q_{p+1}|\) 的大小关系:

  若 \(|Q_p|<|Q_{p+1}|\),注意到此时必然有 \(|Q_{p+1}|=2|Q_p|+1\),\(\delta_p\) 的当务之急是让每个 \(q\in Q_{p+1}\) 都有前驱。因此先让 \(Q_p\) 内每个点随便在 \(Q_{p+1}\) 里连两个,剩下一个状态放弃一个转移最小化 \(|L|\),增加一个到 \(Q_{p+1}\) 内后缀最多的状态的转移最大化 \(|L|\)。

  若 \(|Q_p|\ge|Q_{p+1}|\),我们需要同时关注“每个状态都有前驱”以及“最小/最大化 \(L\)”的要求。先描述出“在 \(Q_{p+1}\) 里连两个后继”的贡献,令 \(F(x)\) 表示连接方案数关于连接到后继的后缀总数的 GF,那么 \(F(x)=(G_{p+1}(x)+1)^2-1=(x+1)^{2^{m-p+1}}-1\)。接着贪心考虑最小化与最大化 \(L\) 的构造方案:

  • 最小化 \(|L|\),先用 \(Q_p\) 内的 \(|Q_{p+1}|\) 个状态直接各自连一条转移边到 \(Q_{p+1}\) 内的状态,求到剩下能选的连接方案的 GF \(F_1(x)\),在 \(F_1(x)\) 里按指标升序贪心选择。
  • 最大化 \(|L|\),先用 \(Q_p\) 内的 \(|Q_{p+1}|\) 个状态直接各自连一条转移边到 \(Q_{p+1}\) 内的状态,再连一条转移边到 \(Q_{p+1}\) 内后缀串最多的状态,其余和最小化类似。

  撇开高精度计算,复杂度是 \(\mathcal O(n)\) 的。

\(\mathcal{Code}\)

# Rainybunny #

import sys

if __name__ == "__main__":
sys.stdin = open('dfa.in', 'r')
sys.stdout = open('dfa.out', 'w') n = int(input())
ans = [0 for _ in range(3)] m = 0
while 1 << m + 1 <= n - m - 1: m += 1
## the last limited level's G.F. is f_m(x)=(x+1)^{2^m}-1.
# sys.stderr.write(str(m) + '\n') ans[0] = (1 << n - m) - 1
for i in range(0, m + 1):
ans[0] += (1 << (1 << i)) - 1 lef = 1 << n - m - 1; rig = (1 << (1 << m)) - 1
if (lef < rig):
sys.stderr.write('type 1\n')
ans[1] = 1 << ((1 << m) + m - 1)
ans[2] = ans[1] + (1 << m)
else:
sys.stderr.write('type 2\n')
## get f_m(x)(->bino[0]) and f_m^2(x)(->bino[1])
bino = [[0 for _ in range((1 << m) + 1)],
[0 for _ in range((1 << m + 1) + 1)]]
bino[0][0] = bino[1][0] = 1
for i in range(1, (1 << m) + 1):
bino[0][i] = bino[0][i - 1] * ((1 << m) - i + 1) // i
for i in range(1, (1 << m + 1) + 1):
bino[1][i] = bino[1][i - 1] * ((1 << m + 1) - i + 1) // i ## get ans[1].
for i in range(1, (1 << m) + 1):
ans[1] += i * bino[0][i]
bino[1][i] -= bino[0][i]
rest = lef - rig; cur = 1
while rest and rest > bino[1][cur]:
ans[1] += bino[1][cur] * cur
rest -= bino[1][cur]
cur += 1
ans[1] += cur * rest ## get ans[2].
for i in range(1, (1 << m) + 1):
ans[2] += bino[0][i] * (i + (1 << m))
bino[1][i] += bino[0][i] # recover it
bino[1][i + (1 << m)] -= bino[0][i]
rest = lef - rig; cur = 1 << m + 1
while rest and rest > bino[1][cur]:
ans[2] += bino[1][cur] * cur
rest -= bino[1][cur]
cur -= 1
ans[2] += cur * rest print("%d %d %d" % (ans[0], ans[1], ans[2])) sys.stdin.close()
sys.stdout.close()

Solution -「LOCAL」Minimal DFA的更多相关文章

  1. Solution -「LOCAL」二进制的世界

    \(\mathcal{Description}\)   OurOJ.   给定序列 \(\{a_n\}\) 和一个二元运算 \(\operatorname{op}\in\{\operatorname{ ...

  2. Solution -「LOCAL」大括号树

    \(\mathcal{Description}\)   OurTeam & OurOJ.   给定一棵 \(n\) 个顶点的树,每个顶点标有字符 ( 或 ).将从 \(u\) 到 \(v\) ...

  3. Solution -「LOCAL」过河

    \(\mathcal{Description}\)   一段坐标轴 \([0,L]\),从 \(0\) 出发,每次可以 \(+a\) 或 \(-b\),但不能越出 \([0,L]\).求可达的整点数. ...

  4. Solution -「LOCAL」Drainage System

    \(\mathcal{Description}\)   合并果子,初始果子的权值在 \(1\sim n\) 之间,权值为 \(i\) 的有 \(a_i\) 个.每次可以挑 \(x\in[L,R]\) ...

  5. Solution -「LOCAL」Burning Flowers

      灼之花好评,条条生日快乐(假装现在 8.15)! \(\mathcal{Description}\)   给定一棵以 \(1\) 为根的树,第 \(i\) 个结点有颜色 \(c_i\) 和光亮值 ...

  6. Solution -「LOCAL」画画图

    \(\mathcal{Description}\)   OurTeam.   给定一棵 \(n\) 个点的树形随机的带边权树,求所有含奇数条边的路径中位数之和.树形生成方式为随机取不连通两点连边直到全 ...

  7. Solution -「LOCAL」ZB 平衡树

    \(\mathcal{Description}\)   OurOJ.   维护一列二元组 \((a,b)\),给定初始 \(n\) 个元素,接下来 \(m\) 次操作: 在某个位置插入一个二元组: 翻 ...

  8. Solution -「LOCAL」舟游

    \(\mathcal{Description}\)   \(n\) 中卡牌,每种三张.对于一次 \(m\) 连抽,前 \(m-1\) 次抽到第 \(i\) 种的概率是 \(p_i\),第 \(m\) ...

  9. Solution -「LOCAL」充电

    \(\mathcal{Description}\)   给定 \(n,m,p\),求序列 \(\{a_n\}\) 的数量,满足 \((\forall i\in[1,n])(a_i\in[1,m])\l ...

  10. Solution -「LOCAL」「cov. 牛客多校 2020 第五场 C」Easy

    \(\mathcal{Description}\)   Link.(完全一致)   给定 \(n,m,k\),对于两个长度为 \(k\) 的满足 \(\left(\sum_{i=0}^ka_i=n\r ...

随机推荐

  1. Python 爬取广州商学院新闻----测试版

    Python 爬取广州商学院新闻----测试版 程序简述:抓取广州商学院新闻栏目的全部新闻内容 开发环境:PyCharm Community Edition 爬取连接:http://news.gzcc ...

  2. base64编码图片二进制数据后直接保存在html文件中

    相关内容: 在markdown编辑器中嵌入base64图片 看到一个帖子,那就是base64编码用来http服务中对二进制文件编码,那么可以不可以直接在html文件中使用base64编码后的字符串来表 ...

  3. Netty 如何自动探测内存泄露的发生

    本文基于 Netty 4.1.112.Final 版本进行讨论 本文是 Netty 内存管理系列的最后一篇文章,在第一篇文章 <聊一聊 Netty 数据搬运工 ByteBuf 体系的设计与实现& ...

  4. CUDA编程学习 (2)——CUDA并行性模型

    1. 基于 kernel 的 SPMD 并行编程 1.1 向量加法 kernel(device 代码) // Device Code // Compute vector sum C = A + B / ...

  5. CSP2023总结

    CSP2023总结 入门组 比赛开始时,顺序开题. 看完 \(T1\) 后没有立即想出正确解法,有点慌乱,看完 \(T2,T3\) 并思考了一会后过去了大概 \(20\) 分钟,想到了解法便看 \(T ...

  6. Java Cache系列之Cache概述和Simple Cache

    前记:最近公司在做的项目完全基于Cache(Gemfire)构建了一个类数据库的系统,自己做的一个小项目里用过Guava的Cache,以前做过的项目中使用过EHCache,既然和Cache那么有缘,那 ...

  7. Java 并发编程实战学习笔记——寻找可强化的并行性

    寻找可强化的并行性 1.串行执行所有任务 代码 复制 - 运行 package net.jcip.examples; import java.util.*; /** * SingleThreadRen ...

  8. Dockerfile&Docker-Compose之基础

    使用了很久的docker,之前却从来没有总结过, 于是开此篇来记录平常使用Dockerfile和docker-compose.yaml的点滴, 先从基础命令开始哦 [Dockerfile] Docke ...

  9. ssh之秘钥登陆

    前提: 1. 秘钥的生成需要OpenSSL的支持, 需要自行进行安装 一. 新建用户 在root登陆状态中执行命令: useradd -m ssh-user # centosadduser ssh-u ...

  10. Threejs的三维坐标系

    在三维空间中,所有的物体和相机都需要基于一个统一的坐标系来进行定位和操作.理解坐标系的基本概念,对于创建稳定.准确的三维效果至关重要. 基础 Three.js 采用的是右手坐标系,这意味着如果你将右手 ...