參考:
對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的等價類個數
並且顯然如果,那麼和等價
(這樣的話和無論接受到什麼信息都會轉移到同一個狀態上)
顯然地,如果m和k互質,n=k
討論n和k不互質的情況
由於我們要維護序號爲0~n的[0,n)個狀態,所以一個等價類得只取最小那個點
0是獨立的等價類。所以我們只要對[1,k-1]進行操作
記
表示這一輪我們在[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(1)~h(取遍了中d的倍數
所以每一輪
首先每個數都要再乘上一次m
如果L>
乘上m之後,答案是會有重複的;
並且我們已經得知了在上一層有等價類(L,K)
那麼這一層就得去掉上一層等價類能推到的個數;
剩下的數除以d得到新的L=(0,]
注意一下K-m(K-L)可能小於等於0 這個時候我們得返回一個
判斷K-m(K-L)小於等於0的時候
可以先轉成double進行運算 也可以把乘移位一下變成除(當然除會比乘慢。。
因爲long long進行乘法運算的範圍小於double
用long long乘容易出界。
我們將K更改爲,繼續遞歸調用f(L,K)
如果L<=
刪掉了所有L個數。這些數要統計到答案裏,於是返回L
然後把上面統計的答案,再加上最開始獨立的等價類0就得到了我們要的最小n
也就是最小化DFA的狀態數量
DFA最小化就這麼完成了(
每組數據複雜度
總複雜度
其實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;
}
好久以前寫的東西啦