Gym226005-G丨数论丨质因数分解丨Weird Requirements

>>原题链接

一开始完全没有思路,和学长交流过以后,对质因数分解有了更深的理解了,是很棒的理解的题目哦!学长的题解比较简洁,我不知不觉写得很详细了,就贴出来吧

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int T,n,x,y,revise,keyrev;
ll tmp,gcd,lcm,revgcd,revlcm;

/* http://codeforces.com/gym/226005/problem/G
 * 这题要用到质因数分解的思路,我觉得蛮重要的
 * 首先要知道所有数都可以分解成一系列质数的积,其中相同的质因数可以合并成幂指数
 * 多个数的gcd,就是这些同底指数取最小值的幂的乘积,而lcm是最大值的情况
 *
 * 这题要求所给的数组的整体gcd==x,lcm==y,求最少要求改多少才能达成条件
 * 最理想情况是,逐项求gcd,lcm,都满足条件。
 * 考虑一种意外,x不是ai的因子,y不是ai的倍数,这是格格不入的一个数,肯定要修改
 * 这样所有数读一遍,求出符合x是ai的因子,y是ai的倍数的数组的总体gcd和lcm,并统计revise的数量
 *
 * 但是这个时候,还需要注意,此时的gcd和x,lcm和y不一定就相等
 * 举个例子,revise=0,但是gcd!=x,lcm!=y,这种情况还需要修改,我们记作keyrev
 * 这样的情况怎么修改呢?
 * 由gcd是取质因子指数最小的乘积,gcd!=x说明需要一个数使得因子的最小值满足x的情况
 * 同理也需要一个数, 使得因子的最小值满足y的情况
 *
 * 这样可以想到,至少需要keyrev=2个数字修改
 * 但是如果,需要改变指数最小值的质因子 和 需要改变指数最大值的质因子 没有是不同的呢?
 * 那么只需要修改一个数字,同时包含最小和最大指数的质因子就Ok啦
 *
 * 分析几个小样例:(gcd,lcm表示当前满足tmp%x==0&&y%tmp==0的总值)
 * #1:
 * 2 30 900
 * 90 450
 *  90= 2^1 * 3^2 * 5^1;
 * 450= 2^1 * 3^2 * 5^2;
 * gcd= 2^1 * 3^2 * 5^1 = 90;
 * lcm= 2^1 * 3^2 * 5^2 = 450;
 *   x= 2^1 * 3^1 * 5^1 = 30;
 * 需要修改一个数,使得3的最小指数为1;
 *   y=2^2 * 3^2 * 5^2 = 450;
 * 需要修改一个数,使得2的最大指数为2;
 * 这时候,两个质因子是不重合的,可以同时改变2的最大指数,3的最小指数。
 * 所以试试看把90改成60,是不是就满足条件啦?
 *
 * #2:
 * 4 1 900
 * 5 30 180 7
 *   5= 2^0 * 3^0 * 5^1;
 *  30= 2^1 * 3^1 * 5^1;
 * 180= 2^2 * 3^2 * 5^1;
 *   7= 7^1;(不可能凑出满足xy的情况,必修改)
 * gcd= 2^0 * 3^0 * 5^1 = 5;
 * lcm= 2^2 * 3^2 * 5^1 =180;
 *   x= 2^0 * 3^0 * 5^0 = 1;
 * 需要修改一个数,使得5的最小指数为0;
 *   y= 2^2 * 3^2 * 5^2 = 900;
 * 需要修改一个数,使得5的最大指数为2;
 * 显然只修改一个指数是不够的呀,那就只能修改两个了!
 * (而不是只修改7数字)
 *
 * 这道题也就是把每个数字都看成一列质因数的乘积!这样想下去就会慢慢清晰啦!
 */
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        keyrev=revise=0;
        gcd=lcm=0;
        scanf("%d %d %d ",&n,&x,&y);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&tmp);
            if(tmp%x==0&&y%tmp==0){
                if(!gcd&&!lcm){
                    gcd=lcm=tmp;
                } else {
                    gcd=__gcd(gcd,tmp);//gcd表示当前满足x,y的数字的gcd
                    lcm=lcm*tmp/__gcd(lcm,tmp);//同理
                }
            } else //不满足x,y就是必须要修改了
                revise++;
        }

        //printf("%d %d\n",x,y);
        if((n==1&&x!=y)||y%x!=0)puts("-1");
        else if(!gcd||!lcm)printf("%d\n",revise);
        else {
            keyrev=2;//默认假设需要两处关键修改,使得整体gcd==x,lcm==y
            revgcd=gcd/x;//为了达到整体gcd为x,需要修改掉的多余的因子
            revlcm=y/lcm;//为了达到整体lcm为y,需要修改加上还差的因子

            if(gcd==x&&lcm==y){//已经达到x,y 不需要关键修改了
                keyrev=0;
            } else if(__gcd(revgcd,revlcm)==1){//两个因子没有重合部分,可以通过修改一个数字,使得质因子的max与min都满足
                keyrev=1;
            }//因子有重合部分,只修改一个不能同时调整质因子的min和max,所以是默认的修改两次~
            printf("%d\n",max(revise,keyrev));
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值