POJ 3101 Astronomy(分子最小公倍数+圆周追击)

Description

There are n planets in the planetary system of star X. They orbit star X in circular orbits located in the same plane. Their tangent velocities are constant. Directions of orbiting of all planets are the same.

Sometimes the event happens in this planetary system which is called planet parade. It is the moment when all planets and star X are located on the same straight line.

Your task is to find the length of the time interval between two consecutive planet parades.

Input

The first line of the input file contains n — the number of planets (2 ≤ n ≤ 1 000).

Second line contains n integer numbers ti — the orbiting periods of planets (1 ≤ ti ≤ 10 000). Not all of ti are the same.

Output

Output the answer as a common irreducible fraction, separate numerator and denominator by a space.

Sample Input

3
6 2 3
Sample Output

3 1
Hint

在这里插入图片描述

题意

  有n个卫星,绕一个恒星做圆周运动,已知他们的周期,询问连续两次卫星在一条直线上的最小时间差。

思路

  先分析两个卫星的情况,假设他们的周期分别是a,b,最小时间差是t,那么相对加速度乘以 t 就是 π,|2π/a - 2π/b| * t = π,也就是t = a * b / (2 * |a-b|)
  n个卫星的话,就有多个t值,所以每隔所有t值的最小公倍数的时间所有的卫星就会在同一直线上!
假设他的分子分别是a1、a2 …… ,分母是b1,b2 ……(仅是|a-b|),那么分数的最小公倍数就是:lcm(a1,a2……) / 2 * gcd(b1,b2……)
  1. 这里求解每组的|a-b|的时候,让a==q[0](第一个卫星的周期),然后for(1->n-1) tmp = |q[i] - q[0]|求解,

	scanf("%lld",&q[0]);	//a值输入
    for(int i=1;i<n;i++){
        scanf("%lld",q+i);
        //求解|a-b|
        if(q[i]-q[0]>=0)    tmp = q[i]-q[0];
        else    tmp = q[0]-q[i];
        //分数约分
        if(tmp!=0){
            ll g = gcd(tmp,q[0]*q[i]);	//gcd(|a-b|,a*b)
            cal(q[0]*q[i]/g);	//质因分解a*b,以便最后求解lcm
            b[i] = tmp / g;	//存下约分后的分子
        }
        else    b[i] = 0;	//tmp==0,直接分子,分母是0
    }
	//ans2 = gcd(b~1~,b~2~……)
    ll ans2 = 0;
    for(int i=1;i<n;i++)
        ans2 = gcd(ans2,b[i]);	
	//这里牵扯的到的是:lcm(a~1~,a~2~……) / 2 * gcd(b~1~,b~2~……),是分子/2,还是分母除/2
    if(cnt[1]>0)    cnt[1]--;	//分子有2这个因子,分子/2
    else    ans2 *= 2;	//分母*2

  这样我们的分母就求出来了,关键是分子的求解,我们知道题目给出的数据范围,分子运算中会超long long的,我们不用java,就只能高精度乘法了。
  2.先说一下质因分解函数的作用,根据质因分解,我们知道每个数都是由素数的乘积得到的,所以我们先筛一遍素数(pri数组,tot),并记录除以每个pri[i]时的个数temp,并更新该个数的最大值cnt[i] = max(temp, cnt[i]),最后cnt[i]个pri[i]分别相乘得到最终的结果就是所有数的最小公倍数!

const int maxn = 10100;
ll pri[maxn],cnt[maxn];
bool v[maxn];
int tot;
void primes()	//欧拉筛素数
{
    tot = 0;
    memset(v,true,sizeof(v));
    v[1] = false;
    for(int i=2;i<maxn;i++){
        if(v[i])    pri[++tot] = (ll)i;
        for(int j=1;i*(int)pri[j]<maxn&&j<=tot;j++){
            v[i*(int)pri[j]] = 0;
            if(i%(int)pri[j]==0)    break;
        }
    }
}
void cal(ll num)
{
    ll tmp;
    for(int i=1;pri[i]<=num;i++){
        tmp = 0;
        while(num%pri[i]==0){	//找质因子个数
            ++tmp;
            num /= pri[i];	
        }
        if(tmp>cnt[i])  cnt[i] = tmp;	//找最大值
    }	
}

  这样的话我们就只剩下求解lcm(分子)的任务了,=.=,看似轻松,这里的话b数组记录的是分子,为了省事,每一个b[i]记录4位十进制,这样的话,要注意一下最后的输出,除了最高位,都要%04lld。

void mul()
{
    ll c = 0;	//余数
    a[0] = 1;
    pp = 0;	//位数
    for(int i=1;i<=tot;i++){
        for(int j=0;j<cnt[i];j++){
            for(int k=0;k<=pp;k++){
                a[k] = a[k]*pri[i]+c;
                c = a[k]/10000;
                a[k] %= 10000;
            }
            if(c>0){
                a[++pp] = c;
                c = 0;
            }
        }
    }
}

AC代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<ctime>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 10100;
ll pri[maxn],cnt[maxn],q[maxn];
ll a[maxn],b[maxn];
bool v[maxn];
int n,tot,pp;
void primes()
{
    tot = 0;
    memset(v,true,sizeof(v));
    v[1] = false;
    for(int i=2;i<maxn;i++){
        if(v[i])    pri[++tot] = (ll)i;
        for(int j=1;i*(int)pri[j]<maxn&&j<=tot;j++){
            v[i*(int)pri[j]] = 0;
            if(i%(int)pri[j]==0)    break;
        }
    }
}
ll gcd(ll a,ll b)
{
    return b==0 ? a : gcd(b,a%b);
}
void cal(ll num)
{
    ll tmp;
    for(int i=1;pri[i]<=num;i++){
        tmp = 0;
        while(num%pri[i]==0){
            ++tmp;
            num /= pri[i];
        }
        if(tmp>cnt[i])  cnt[i] = tmp;
    }
}
void mul()
{
    ll c = 0;
    a[0] = 1;
    pp = 0;
    for(int i=1;i<=tot;i++){
        for(int j=0;j<cnt[i];j++){
            for(int k=0;k<=pp;k++){
                a[k] = a[k]*pri[i]+c;
                c = a[k]/10000;
                a[k] %= 10000;
            }
            if(c>0){
                a[++pp] = c;
                c = 0;
            }
        }
    }
}
int main(void)
{
    ll tmp;
    primes();
    memset(cnt,0,sizeof(cnt));
    scanf("%d",&n);
    scanf("%lld",&q[0]);
    for(int i=1;i<n;i++){
        scanf("%lld",q+i);
        if(q[i]-q[0]>=0)    tmp = q[i]-q[0];
        else    tmp = q[0]-q[i];
        if(tmp!=0){
            ll g = gcd(tmp,q[0]*q[i]);
            cal(q[0]*q[i]/g);
            b[i] = tmp / g;
        }
        else    b[i] = 0;
    }

    ll ans2 = 0;
    for(int i=1;i<n;i++)
        ans2 = gcd(ans2,b[i]);

    if(cnt[1]>0)    cnt[1]--;
    else    ans2 *= 2;

    mul();

    printf("%lld",a[pp]);		//**注意
    for(int i=pp-1;i>=0;i--)	//**注意
        printf("%04lld",a[i]);	//**注意
    printf(" %lld",ans2);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃夭丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值