Solution -「LOCAL」Minimal DFA
\(\mathscr{Description}\)
令 \(\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的更多相关文章
- Solution -「LOCAL」二进制的世界
\(\mathcal{Description}\) OurOJ. 给定序列 \(\{a_n\}\) 和一个二元运算 \(\operatorname{op}\in\{\operatorname{ ...
- Solution -「LOCAL」大括号树
\(\mathcal{Description}\) OurTeam & OurOJ. 给定一棵 \(n\) 个顶点的树,每个顶点标有字符 ( 或 ).将从 \(u\) 到 \(v\) ...
- Solution -「LOCAL」过河
\(\mathcal{Description}\) 一段坐标轴 \([0,L]\),从 \(0\) 出发,每次可以 \(+a\) 或 \(-b\),但不能越出 \([0,L]\).求可达的整点数. ...
- Solution -「LOCAL」Drainage System
\(\mathcal{Description}\) 合并果子,初始果子的权值在 \(1\sim n\) 之间,权值为 \(i\) 的有 \(a_i\) 个.每次可以挑 \(x\in[L,R]\) ...
- Solution -「LOCAL」Burning Flowers
灼之花好评,条条生日快乐(假装现在 8.15)! \(\mathcal{Description}\) 给定一棵以 \(1\) 为根的树,第 \(i\) 个结点有颜色 \(c_i\) 和光亮值 ...
- Solution -「LOCAL」画画图
\(\mathcal{Description}\) OurTeam. 给定一棵 \(n\) 个点的树形随机的带边权树,求所有含奇数条边的路径中位数之和.树形生成方式为随机取不连通两点连边直到全 ...
- Solution -「LOCAL」ZB 平衡树
\(\mathcal{Description}\) OurOJ. 维护一列二元组 \((a,b)\),给定初始 \(n\) 个元素,接下来 \(m\) 次操作: 在某个位置插入一个二元组: 翻 ...
- Solution -「LOCAL」舟游
\(\mathcal{Description}\) \(n\) 中卡牌,每种三张.对于一次 \(m\) 连抽,前 \(m-1\) 次抽到第 \(i\) 种的概率是 \(p_i\),第 \(m\) ...
- Solution -「LOCAL」充电
\(\mathcal{Description}\) 给定 \(n,m,p\),求序列 \(\{a_n\}\) 的数量,满足 \((\forall i\in[1,n])(a_i\in[1,m])\l ...
- Solution -「LOCAL」「cov. 牛客多校 2020 第五场 C」Easy
\(\mathcal{Description}\) Link.(完全一致) 给定 \(n,m,k\),对于两个长度为 \(k\) 的满足 \(\left(\sum_{i=0}^ka_i=n\r ...
随机推荐
- [解决方案] 几种通过 iproute2 来打通不同节点间容器网络的方式
几种通过 iproute2 来打通不同节点间容器网络的方式 几种通过 iproute2 来打通不同节点间容器网络的方式 host-gw ipip vxlan 背景 之前由于需要打通不同节点间的容器网络 ...
- Python 抓取猫眼电影排行
import json import re import requests from requests.exceptions import RequestException import time # ...
- 题解:CF1015D Walking Between Houses
题解:CF1015D Walking Between Houses 算法 模拟,分类讨论 分析 首先,设每步走的距离为 \(t_i\),我们发现 \(t_i\) 应是满足 \(1\le t_i\le ...
- apisix~限流插件的使用
参考: https://i4t.com/19399.html https://github.com/apache/apisix/issues/9193 https://github.com/apach ...
- 测试App出现闪退应对方法
1.使用adb logcat 查看日志,使用adb logcat -f sdcard/log.txt(输出到手机上) ,先查看报错信息 2.保证主线程能够继续运行,避免在子线程中与UI交互 3.内存溢 ...
- P5987 [PA2019] Terytoria / 2023NOIP A层联测13 T3 全球覆盖
P5987 [PA2019] Terytoria / 2023NOIP A层联测13 T3 全球覆盖 题面及数据范围 对于一个点对,可以降维为线段,转化为 1 维的问题. 如图: 我们可以在横着的方向 ...
- Java 日志缓存机制的实现
本文介绍了 Java 日志机制,并通过扩展日志功能,实现日志缓存.同时,可以动态控制日志信息打印到日志文件的级别,从而提高了日志的灵活性,给产品质量带来更大的帮助.文章主要围绕 MemoryHandl ...
- Java中的equals()和hashCode()契约
本文由 ImportNew - 唐小娟 翻译自 Programcreek.如需转载本文,请先参见文章末尾处的转载要求. ImportNew注:如果你也对Java技术翻译分享感兴趣,欢迎加入我们的 Ja ...
- Impala源代码分析(3)-backend查询执行过程
4 Replies 这篇文章主要介绍impala-backend是怎么执行一个SQL Query的. 在Impala中SQL Query的入口函数是: void ImpalaServer::query ...
- Linux之轨迹记录(script)
使用命令: script 编辑文件: vim /etc/profile 在最后一行添加命令 if [ $UID -ge 0 ]; then exec /usr/bin/script -t 2>/ ...