洗澡回来遇到个好玩的数论题,想了想还是决定写篇博客来说说这个题。。。ORZ
hdu-5902
这个题呢,说给你n个数(n<=500),每个数小于等于1000,然后每次从这些数里面任意挑选出三个数,然后随意取这三个数里面的两个数进行gcd得到数d,然后把d,d加进数组里,(对,没错,就是加两次),然后操作到最后一定是两个一样的数,问你最后剩下的数可能是几,,,
具体题意:
Alex发明了一个有趣的游戏. 一开始他在黑板上写了n个正整数, 然后他开始重复进行如下的操作:
- 他选择黑板上三个数字a, b和c, 把他们从黑板上擦掉.
- 他从这三个数a, b和c中选择了两个数, 并计算出他们的最大公约数, 记这个数为d (d 可以是gcd(a,b), gcd(a,c)或者gcd(b, c)).
- 他在黑板上写下两次数字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;
}