BZOJ传送门
洛谷传送门
解析:
这道题本质其实是要最小化一个能够接受所有 m m m进制下 K K K的倍数的自动机。
显然我们有一个方案就是 K K K个节点, i i i的第 j j j条边向 ( i ∗ m + j ) % K (i*m+j)\%K (i∗m+j)%K连过去。
所以我们有平凡的上界 K K K,现在考虑合并自动机节点。
两个节点是等价的,当且仅当它们的所有能够接受的后继转移相同。
换句话说,设
f
(
a
)
f(a)
f(a)是
a
a
a状态转移到
0
0
0的最小步数,如果
a
,
b
a,b
a,b等价,则
f
(
a
)
=
f
(
b
)
f(a)=f(b)
f(a)=f(b)且
a
∗
m
f
(
a
)
≡
b
∗
m
f
(
b
)
(
m
o
d
K
)
a*m^{f(a)}\equiv b*m^{f(b)}\pmod K
a∗mf(a)≡b∗mf(b)(modK),反之也成立。
显然 0 0 0是一个单独的等价类,同时是开始状态和终止状态。
现在考虑去掉等价的状态,将所有数直接乘上 m m m看落在 % K \%K %K的哪个等价类中。这时候 [ K − m + 1 , K ] [K-m+1,K] [K−m+1,K]中的数就是 f ( i ) = 1 f(i)=1 f(i)=1的,令 d = gcd ( K , m ) d=\gcd(K,m) d=gcd(K,m),此时所有数都是 d d d的倍数。
显然,如果 d = 1 d=1 d=1,所有数对应不同的等价类,没有可以删去的数,不然就将 [ K − m + 1 , K ] [K-m+1,K] [K−m+1,K]中的数去重并加入答案。
然后再将剩下的数 ∗ m *m ∗m,将 [ K − m 2 + 1 , K ] [K-m^2+1,K] [K−m2+1,K]中的数去重并加入答案。
令 s o l v e ( L , m , K ) solve(L,m,K) solve(L,m,K)表示保留 1 − L 1-L 1−L中的数的时候的答案。
如果 L ≤ K / d L\leq K/d L≤K/d,所有数都会对应不同的值,没有等价的。
否则, i m % K im\%K im%K取遍了 K / d K/d K/d的所有值,剩下 [ 1 , K − m ( K − L ) ] [1,K-m(K-L)] [1,K−m(K−L)]中 d d d的倍数,递归处理就好了。
注意如果直接递归处理, m ∗ ( K − L ) m*(K-L) m∗(K−L)的乘法会爆 l o n g l o n g long long longlong,所以先判断一下区间内有没有数再继续做。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline ll getint(){
re char c;
while(!isdigit(c=gc()));re ll num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
inline ll solve(ll L,ll m,ll K){
ll d;
if((d=__gcd(m,K))==1)return L;
if(L<=K/d)return L;
if(K/(K-L)>=m)return m/d*(K-L)+solve(K/d-m/d*(K-L),m,K/d);
else return K/d;
}
int T;
ll m,K;
signed main(){
T=getint();
while(T--){
m=getint(),K=getint();
cout<<solve(K-1,m,K)+1<<"\n";
}
return 0;
}