【BZOJ】5213: [Zjoi2018]迷宫-DFA&找规律

传送门:bzoj5213


题解

问题的本质是,计算能识别所有 m m m进制下 k k k的倍数的确定性有限状态自动机(DFA)的最小点数。— 官方题解

首先存在答案上界 k k k:构造 k k k个点(标号为 [ 0 , k ) [0,k) [0,k)),第 i i i点依次代表 m o d   k = i mod\ k=i mod k=i,第 i i i个点的第 j j j条出边连向 i ⋅ m + j m o d    k i·m+j \mod k im+jmodk

答案就是上述自动机节点的等价类个数。点 i , j i,j i,j等价当且仅当 i i i出发能接受的所有 m m m进制数集合和 j j j完全相同。

f ( i ) f(i) f(i)表示从点 i i i出发,能接受的最短的 m m m进制数的位数, g ( i ) = i ⋅ m f ( i ) ( m o d k ) g(i)=i·m^{f(i)}\pmod k g(i)=imf(i)(modk)

则点 i , j i,j i,j等价的充要条件即 f ( i ) = f ( j ) f(i)=f(j) f(i)=f(j) g ( i ) = g ( j ) g(i)=g(j) g(i)=g(j)(不会证明)。

  • 0 0 0必然是单独的一个等价类,设 f ( l , m , k ) f(l,m,k) f(l,m,k)表示在制定的 m , k m,k m,k下还剩下 1 , 2 , . . . , l 1,2,...,l 1,2,...,l的等价类个数。

  • 把这些数 × m   m o d   k \times m \ mod \ k ×m mod k,若两个数操作后相等,则它们必然在一个等价类中,直接去重。设 d = g c d ( m , k ) d=gcd(m,k) d=gcd(m,k),则所有数均为 d d d的倍数。

  • 此时所有 > k − m >k-m >km的数和 0 0 0都分别代表一个等价类, f f f = 1 =1 =1,把这些数删去。

  • 对于剩下的数,再 × m   m o d   k \times m \ mod \ k ×m mod k,去重后删去 > k − m 2 >k-m^2 >km2的数,依次类推。

具体来说,设初始状态为 f ( k − 1 , m , k ) f(k-1,m,k) f(k1,m,k),每一层迭代删去 f f f逐次 + 1 +1 +1的等价类:

  • d = g c d ( m , k ) d=gcd(m,k) d=gcd(m,k),若 d = 1 d=1 d=1,显然答案为 l l l,否则将 [ 1 , l ] [1,l] [1,l] × m   m o d   k \times m \ mod \ k ×m mod k
  • 上一层删去了 ( l , k ) (l,k) (l,k)中的数,则这一轮中要删去在 ( k − m ( k − l ) , k ) ∪ { 0 } (k-m(k-l),k)\cup \{0\} (km(kl),k){0}中的数:
    ( 1 ) (1) (1) k ≤ m ( k − l ) k\leq m(k-l) km(kl),显然答案为 l l l
    ( 2 ) (2) (2) l > k d l>\frac kd l>dk,则剩下的是 ( 0 , k − m ( k − l ) ] (0,k-m(k-l)] (0,km(kl)] d d d的倍数,删去了 m ( k − l ) d \frac{m(k-l)}{d} dm(kl)个数,递归子问题 f ( k − m ( k − l ) d , m , k d ) f(\frac{k-m(k-l)}{d},m,\frac{k}{d}) f(dkm(kl),m,dk)
    ( 3 ) (3) (3) l ≤ k d l\leq \frac kd ldk,则 l ≤ k d ⋅ d ( m − 1 ) m l\leq \frac kd ·\frac{d(m-1)}{m} ldkmd(m1),则 k ≤ m ( k − l ) k\leq m(k-l) km(kl),转成情况 ( 1 ) (1) (1)

总时间复杂度 O ( T log ⁡ k ) O(T\log k) O(Tlogk)


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int T;ll m,k;

inline ll gcd(ll x,ll y){return (!y)?x:gcd(y,x%y);}

ll sol(ll l,ll k)
{
	ll d=gcd(m,k);if(d==1 || l<=k/d) return l;
	if(k<=(double)m*(k-l)) return k/d;
	return m/d*(k-l)+sol((k-m*(k-l))/d,k/d);
}

int main(){
	for(scanf("%d",&T);T;--T){
		scanf("%lld%lld",&m,&k);
		printf("%lld\n",sol(k-1,k)+1);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值