2019.03.04【ZJOI2018】【BZOJ5213】【洛谷P4339】迷宫(自动机)(数论)

BZOJ传送门

洛谷传送门


解析:

这道题本质其实是要最小化一个能够接受所有 m m m进制下 K K K的倍数的自动机。

显然我们有一个方案就是 K K K个节点, i i i的第 j j j条边向 ( i ∗ m + j ) % K (i*m+j)\%K (im+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 amf(a)bmf(b)(modK),反之也成立。

显然 0 0 0是一个单独的等价类,同时是开始状态和终止状态。

现在考虑去掉等价的状态,将所有数直接乘上 m m m看落在 % K \%K %K的哪个等价类中。这时候 [ K − m + 1 , K ] [K-m+1,K] [Km+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] [Km+1,K]中的数去重并加入答案。

然后再将剩下的数 ∗ m *m m,将 [ K − m 2 + 1 , K ] [K-m^2+1,K] [Km2+1,K]中的数去重并加入答案。

s o l v e ( L , m , K ) solve(L,m,K) solve(L,m,K)表示保留 1 − L 1-L 1L中的数的时候的答案。

如果 L ≤ K / d L\leq K/d LK/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,Km(KL)] d d d的倍数,递归处理就好了。

注意如果直接递归处理, m ∗ ( K − L ) m*(K-L) m(KL)的乘法会爆 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值