vj链接
poj链接
不知道为什么poj点进去题目样例什么都没有。。。
题意:
给定一个数N,现在又一个数x,在1~N之间,现在每次可以猜一个数a,返回gcd(x,a),问说最少猜几次可以确定x。
思路:
首先素数筛找到1~n内的素数。我们的第一反映是,想尽量晚猜出来就先猜素数。因为gcd=1我们得不到任何有用的信息。任意一个正整数x=p1^a1*p2^a2*p3^a3……p是素数,a是某次方,即任意一个正整数都能由素数因子的某次方相乘来表示。
比如n=20时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 (素数加粗,标准答案x=12)
我们根据素数从大往小去猜。
gcd(19,x)=1;
gcd(17,x)=1;
gcd(13,x)=1;
…(省略若干)….
gcd(5,x)=1;
gcd(3,x)=3; 到这gcd终于不等于1了,我们找不超过20的最大的3的倍数,是18。gcd(18,x)=3;说明x的素因子3的次方数是1即:x=3^1*p2^a2*p3^a3…..
继续找:gcd(2,x)=2;不超过20的最大的2的倍数是20
gcd(20,x)=4;说明说明x的素因子2的次方数是2
所有的素数都找完了,已经能确定x=2^2*3=12。我们一共猜了10次(8个素数加上两次猜次方数)
但其实对于n=20我们猜6次就可以推测出x了,这就要用到素数分组的方法。
比如14=2*7 我们直接猜gcd(14,x)=2; 就不需要gcd(2,x)和gcd(7,x)都猜一遍了!!!!!猜14与猜2和7有相同的效果!!!可以少猜一次呢!厉害吧!!
类似素数2和7分在一组,我们试着把一些素数分为一组,并且这些素数乘积<=n(超过n的数不能猜),贪心去分,每次从右往左选一个大素数,然后从左往右选一些小素数和大素数相乘,在乘积不超过n的情况下尽量多乘那些小素数(这样能保证组数,也就是最终答案数最小)
这样n=20时的分组为:{19},{17},{2,7},{3,5},{13},{11}
(按组内元素乘积大小排列)一共6组,n=20时这道题的答案就是6。
那为什么在脑子正常的情况下不超过素数组数,就能猜到正确答案呢?
假如对于某个n有一个素数组是{2,5,7}(瞎编的不知道会不会有这种情况)gcd(x,70)=2说明找到了这个x的一个p1为2,同时x不包含5和7这样的素因子,如果是gcd(x,70)=10的话就同时找到了2和5两个p。对于6组都这么操作一遍我们就知道x的所有素因子是什么了,再像之前那样找这些素数的成绩的倍数,就能确定整体的次方数a,然后就能确定了x了!!我觉得答案应该是组数+1,但根据样例n=6,正确答案就是组数2.
至于为什么,玄学,用心体会。(逃)
代码如下:
#include <cstring>
#include <iostream>
#include <string.h>
#include <cstdio>
#include <cstdlib>
using namespace std;
const int MAXN=10010;
int prime[MAXN+1];
int vis[MAXN+1];
void getPrime()
{
memset(prime,0,sizeof(prime));
for(int i=2; i<=MAXN; i++)
{
if(!prime[i])prime[++prime[0]]=i;
for(int j=1; j<=prime[0]&&prime[j]<=MAXN/i; j++)
{
prime[prime[j]*i]=1;
if(i%prime[j]==0) break;
}
}
}
int main()
{
getPrime();
int id=0;
int n;cin>>n;
for(int i=1;i<MAXN;i++)
if(prime[i]>n) {id=i-1;break;}
int cnt=0;
for(int i=1;i<=id;id--)
{
cnt++;
int sum=prime[id];
while(i<id&&sum*prime[i]<=n)
sum*=prime[i++];
}
cout<<cnt<<endl;
}