【BZOJ5213】[ZJOI2018]迷宮(神仙題)
題面
題解
首先可以很容易的得到一個\(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;
}
