参考:
对DFA和NFA的简单理解:http://blog.163.com/ppt_compiler/blog/static/202813007201222873720918/
NFA确定化和DFA最小化:https://blog.csdn.net/u014541281/article/details/52423191
题解:https://blog.csdn.net/qq_16267919/article/details/79675344
https://blog.csdn.net/yfzcsc/article/details/79681391
m进制可行路径%k=0
那么DFA状态上界就是k,分别对应cur%k=[0,k-1]
设k个状态,每个状态有m条出边
状态i的出边j连向状态(im+j)%k
这样的话,只要能够回到0点,这条路径%k就等于0
不过这样构建出来得到的n就不是最小的
所以我们要考虑最小化DFA,那么n就可以最小化,得出答案
最小化DFA需要满足:
1.没有多余的状态
2.没有两个状态是相互等价的
多余状态分两种情况:
1.从这个状态没有通路到达终态
2.从开始状态出发,任何输入串也不能到达的那个状态
两个状态等价需要满足:
1.兼容性(一致性):同是终态或同是非终态
2.传播性(蔓延性):对于所有输入符号,两个状态会转换到等价的状态里
简单解释一下终态和非终态
如果到了状态S,并且没有继续输入了
S为终态,则能够到达NFA终态
S为非终态,不能到达NFA终态
DFA最小化
1.将DFA的状态分为终态和非终态
2.考察每个子集是否再分直到每个子集都不能再分
3.将每一个子集用子集中的某一个状态代替
(注意:如果代替子集的那一个状态有自边,代替之后要保留这个状态的自边;子集中其他状态的互相转化不需要考虑)
所以我们需要得到最小化DFA,就得计算出之前DFA的等价类个数
并且显然如果 ( i 1 ∗ m ) m o d k = ( i 2 ∗ m ) m o d k (i_1*m)\ mod\ k=(i_2*m)\ mod\ k (i1∗m) mod k=(i2∗m) mod k,那么 i 1 i_1 i1和 i 2 i_2 i2等价
(这样的话 i 1 i_1 i1和 i 2 i_2 i2无论接受到什么信息都会转移到同一个状态上)
显然地,如果m和k互质,n=k
讨论n和k不互质的情况
由于我们要维护序号为0~n的[0,n)个状态,所以一个等价类得只取最小那个点
0是独立的等价类。所以我们只要对[1,k-1]进行操作
记 f ( L , K ) f(L,K) f(L,K)
表示这一轮我们在[1,L]中进行删除;K为这一轮的k
也就是说我们上一轮删除了(L,K)的数
以首轮来讲
我们需要对(1,L)中*m%K等价的数进行去重。
剩下的数里(L-m+1,L)的数分别是独立的等价类;
所以答案要加上这些数,然后这些数不会参与下一轮计算。
重复以上过程直到gcd(m,K)=1
我们考虑如何实现f(L,K)的过程。
首先,我们把gcd(m,K)表示成d(
本题最关键的推导↓↓
(虽然我大概会写错什么)
每一轮首先求一下gcd(m,K)如果=1则return L
为了方便表示,记h(i)=i*m mod k
h(i)∈{d|d*j∈[0,K),j∈N}
易得 h ( i ) m o d K d h(i)\ mod\ \frac{K}{d} h(i) mod dK循环
也就是说h(1)~h( K d ) \frac{K}{d}) dK)取遍了 [   0 , K ) [\,0,K) [0,K)中d的倍数
所以每一轮
首先每个数都要再乘上一次m
如果L> k d \frac{k}{d} dk
乘上m之后,答案是会有重复的;
并且我们已经得知了在上一层有等价类(L,K)
那么这一层就得去掉上一层等价类能推到的 m ( K − L ) d \frac{m(K-L)}{d} dm(K−L)个数;
剩下的数除以d得到新的L=(0, K − m ( K − L ) d \frac{K-m(K-L)}{d} dK−m(K−L)]
注意一下K-m(K-L)可能小于等于0 这个时候我们得返回一个 K d \frac{K}{d} dK
判断K-m(K-L)小于等于0的时候
可以先转成double进行运算 也可以把乘移位一下变成除(当然除会比乘慢。。
因为long long进行乘法运算的范围小于double
用long long乘容易出界。
我们将K更改为 K d \frac{K}{d} dK,继续递归调用f(L,K)
如果L<= K d \frac{K}{d} dK
删掉了所有L个数。这些数要统计到答案里,于是返回L
然后把上面统计的答案,再加上最开始独立的等价类0就得到了我们要的最小n
也就是最小化DFA的状态数量
DFA最小化就这么完成了(
每组数据复杂度 O ( l o g k ) O(log\ k) O(log k)
总复杂度 O ( T l o g k ) O(T\ log\ k) O(T log k)
其实DFA最小化并不难理解
最重要的任务还是理解模型的性质(
有不少自动机的东西就算不扯上自动机这个名词也不难懂的
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define ll long long int
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
char frBB[1<<12],*frS=frBB,*frT=frBB;
inline ll __READ()
{
ll x=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return x;
}
#define read() __READ()
int T;
ll m,k;
ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
ll solve(ll L,ll K)
{
ll d=gcd(m,K);
if(d==1)return L;
ll limit=K/d;
if(L>limit)
{
if(K<=1.0*m*(K-L))return limit;
return m*(K-L)/d+solve((K-m*(K-L))/d,limit);
}
return L;
}
int main()
{
T=read();
while(T--)
{
m=read(),k=read();
printf("%lld\n",solve(k-1,k)+1);
}
return 0;
}
好久以前写的东西啦