【BZOJ5213】[ZJOI2018]迷宫(神仙题)

【BZOJ5213】[ZJOI2018]迷宫(神仙题)

题面

BZOJ
洛谷

题解

首先可以很容易的得到一个\(K\)个点的答案。
构建\(K\)个点分别表示\(mod\ K\)的余数。那么点\(i\)的出边\(j\)指向\(i*m+j\ mod\ K\)。容易证明这样子一定是可行的。
但是我们显然还有一部分点是可以丢掉的,即出现点等价的时候,直接合并两个点即可。
那么什么情况下两个点等价呢?显然是两个点可以到达的点集相同的时候是可以直接把这两个点给合并的。
考虑一下\(i*m\)在模\(K\)意义下相等的数的个数,令\(d=gcd(m,K)\),那么合法的取值有\(K/d\)个。定义一个参数\(l\)表示还有\([1,l]\)这些数存在。如果\(l>k/d\),那么在范围内可以取遍所有的合法取值,那么合并这些之后,剩下的部分递归处理,这里删去了\(\frac{m}{d}(k-l)\)个合并之后到数。否则如果\(l\le k/d\),或者\(d=1\),证明必定两两不等,所以这\(l\)个数必须要。

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll m,k;int T;
ll Solve(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)+Solve((k-m*(k-l))/d,k/d);
}
int main()
{
    scanf("%d",&T);
    while(T--)scanf("%lld%lld",&m,&k),printf("%lld\n",Solve(k-1,k)+1);
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/10366991.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值