1. 程式人生 > >【BZOJ5213】[ZJOI2018]迷宮(神仙題)

【BZOJ5213】[ZJOI2018]迷宮(神仙題)

pac 我們 迷宮 http -- geo efi 如果 個數

【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;
}

【BZOJ5213】[ZJOI2018]迷宮(神仙題)