数论+gcd

本文介绍了一道有趣的数论题目,通过分析题目要求,给出了一种有效的算法实现方式。该题要求从n个数中不断选择三个数,通过计算两两之间的最大公约数并重新加入数组,直至最后剩下两个相同的数,探讨可能的结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洗澡回来遇到个好玩的数论题,想了想还是决定写篇博客来说说这个题。。。ORZ

hdu-5902

这个题呢,说给你n个数(n<=500),每个数小于等于1000,然后每次从这些数里面任意挑选出三个数,然后随意取这三个数里面的两个数进行gcd得到数d,然后把d,d加进数组里,(对,没错,就是加两次),然后操作到最后一定是两个一样的数,问你最后剩下的数可能是几,,,

具体题意:
Alex发明了一个有趣的游戏. 一开始他在黑板上写了n个正整数, 然后他开始重复进行如下的操作:

  1. 他选择黑板上三个数字a, b和c, 把他们从黑板上擦掉.
  2. 他从这三个数a, b和c中选择了两个数, 并计算出他们的最大公约数, 记这个数为d (d 可以是gcd(a,b), gcd(a,c)或者gcd(b, c)).
  3. 他在黑板上写下两次数字d.

显然, 在操作n-2次后, 黑板上只会留下两个相同的数字. Alex想要知道哪些数字可以最终留在黑板上.

哈哈,有趣不,,

那么现在来说说思路:

假设某一次放回了x,x,下次操作选x,x,c,可以选择放回gcd(x, x)或者gcd(x,c)。所以答案为n的子集的gcd,因为第一次操作时只能舍弃一个数,因此答案不包括n的gcd,集合元素个数i, 2≤i小于(<)n

所以,以下代码功能为:n个数的集合,求其子集的gcd(不包括本身集合),子集数个数为i,2<=i小于(<)n;

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <queue>
#define ri(n) scanf("%d",&n)
#define oi(n) printf("%d\n",n)
#define rl(n) scanf("%lld",&n)
#define ol(n) printf("%lld\n",n)
#define rep(i,l,r) for(i=l;i<=r;i++)
#define rep1(i,l,r) for(i=l;i<r;i++)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int epg=10-8;
int a[500+10],vis[1000+10];
int gcd(int a,int b)
{
    if(b==0)
        return a;
    else
        return gcd(b,a%b);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<n;i++)
            for(int j=i+1;j<=n;j++)
            vis[gcd(a[i],a[j])]=1;
        int cnt=1,flag=1;
        while(flag&&cnt<=n-3)
        {
            cnt++;
            flag=0;
            for(int i=1;i<=1000;i++)
            {
                if(vis[i])
                {
                    for(int j=1;j<=n;j++)
                    {
                        if(vis[gcd(i,a[j])]==0)
                        {
                            vis[gcd(i,a[j])]=1;
                            flag=1;
                        }
                    }
                }
            }
        }
        int pp=0;
        for(int i=1;i<=1000;i++)
        {
            if(vis[i])
            {
                if(pp)
                    printf(" %d",i);
                else
                {
                    printf("%d",i);
                    pp++;
                }
            }
        }
        printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值