h_nosonの日記

競プロなど

yukicoder No.356 円周上を回る3つの動点の一致

問題
No.356 円周上を回る3つの動点の一致 - yukicoder
円周上を3つの点P1,P2,P3が時計回りに移動している.
それぞれ,T1,T2,T3秒で一周するとき,
同じ点から動き始める時,次に3点が一致するのは何秒後か.

解法
T秒後に\frac{T}{T1}-\frac{T}{T2}が整数かつ\frac{T}{T2}-\frac{T}{T3}が整数ならば,3点が一致する.
まずT1T2の最小公倍数を求め,Tとする.
\frac{T}{T1}-\frac{T}{T2}が1でないとき,T以前に点が一致することがあるので,その値でTを割った時点でもP1P2は一致する.
つまり,P1P2だけなら\large\frac{gcd(T1,T2)}{\frac{gcd(T1,T2)}{T1}-\frac{gcd(T1,T2)}{T2}}秒後に一番先に一致することになる.
P2P3でも同じように求めることができるのでその2つの結果の最小公倍数を求めれば良い.

[-- 追記 --]
解説を見たらこんなことしなくてもよかった.ていうか最後の式約分できる...
\frac{T}{T1}-\frac{T}{T2}\frac{(T2-T1)T3}{T1T2T3}Tと表せて,
もう一つの式と一緒に考えると
T=\frac{T1T2T3}{gcd((T2-T1)T3,(T3-T2)T1)}
を求めるだけでいいらしい.

ソースコード

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define RREP(i,s,e) for (i = s; i >= e; i--)
#define rrep(i,n) RREP(i,(int)(n)-1,0)
#define REP(i,s,e) for (i = s; i <= e; i++)
#define rep(i,n) REP(i,0,(int)(n)-1)
#define INF 100000000

typedef long long ll;

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b,a%b);
}

ll lcm(ll a, ll b) {
    return a * b / gcd(a,b);
}

int main() {
    ll a, b, c;
    ll cm1, cm2; // denominator
    ll dv1, dv2; // numerator
    ll cm;
    ll cd1, cd2;
    cin >> a >> b >> c;
    // T1, T2
    cm1 = lcm(a,b);
    dv1 = abs(cm1 / a - cm1 / b);
    cd1 = gcd(cm1,dv1);
    cm1 /= cd1; dv1 /= cd1; // reduction
    // T2, T3
    cm2 = lcm(b,c);
    dv2 = abs(cm2 / b - cm2 / c);
    cd2 = gcd(cm2,dv2);
    cm2 /= cd2; dv2 /= cd2; // reduction

    cm = lcm(cm1,cm2);
    cout << cm << "/";
    cout << gcd(cm/cm1*dv1,cm/cm2*dv2) << endl;
    return 0;
}