今天要我要讲的是反素数,在ACM中也算是常见的考点,那么对于搞ACM的同学来说,很有必要搞清楚它,所以接下
来我会很详细地讲解。
在讲解反素数之前,我们先来看反素数的概念。
反素数的定义:对于任何正整数,其约数个数记为
,例如
,如果某个正整数
满足:对任意的正整
数,都有
,那么称
为反素数。
从反素数的定义中可以看出两个性质:
(1)一个反素数的所有质因子必然是从2开始的连续若干个质数,因为反素数是保证约数个数为的这个数
尽量小
(2)同样的道理,如果,那么必有
在ACM竞赛中,最常见的问题如下:
(1)给定一个数,求一个最小的正整数
,使得
的约数个数为
(2)求出中约数个数最多的这个数
从上面的性质中可以看出,我们要求最小的,它的约数个数为
,那么可以利用搜索来解。
以前我们求一个数的所有因子也是用搜索,比如,以每一个
为树的一层建立搜索树,深度为
以为例进行说明,建树如下:
可以看出从根节点到每一个叶子结点这条路径上的所有数字乘起来都是12的约数,所以12有6个约数。
搜索的思路就明显了,从根节点开始进行深搜,到叶子结点后再回溯,代码如下:
void dfs(int dept,LL ans = 1)
{
if(dept == cnt)
{
fac[ct++] = ans;
return;
}
for(int i=0;i<=num[dept];i++)
{
dfs(dept+1,ans);
ans *= pri[dept];
}
}<span style="color: windowtext; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
回到我们的问题,同样用搜索来求最小的。
题目:http://codeforces.com/problemset/problem/27/E
题意:给一个数,求一个最小的正整数,使得它的因子个数为。
分析:与求因子的方法类似,先建立搜索树,以每一个为一层建立树型结构,进行搜索,取最小的。
#include<stdio.h>
#define inf (ll)1<<62
typedef long long ll;
int prm[20]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
ll n,ans;
void find(int k,ll cnt,ll sum)
{
int i;
if(sum>n) return;
if(sum==n&&cnt<ans) ans=cnt;
for(i=1;i<=63;i++)
{
if(ans/prm[k]<cnt)
break;
cnt=cnt*prm[k];
find(k+1,cnt,sum*(i+1));
}
}
int main()
{
while(scanf("%I64d",&n)!=EOF)
{
ans=inf;
find(0,1,1);
printf("%I64d\n",ans);
}
return 0;
}<span style="color: windowtext; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1562
题意:求以内的因子最多的那个数。
分析:基本上跟上题差不多。
#include<stdio.h>
#define inf (ll)1<<62
typedef long long ll;
int prm[20]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
ll ans,num,n;
void find(int k,ll cnt,ll sum)
{//k是第k大质因子,cnt是当前的数,sum是当前数的约数个数
int i;
if(k>=16) return ;
if(sum>num)
{
num=sum;
ans=cnt; //如果约数个数更多,将最优解更新为当前数;
}
if(sum==num&&ans>cnt)
ans=cnt;//如果约数个数相同,将最优解更新为较小的数;
for(i=1;i<=63;i++)//开始枚举每个质因子的个数;
{
if(cnt*prm[k]>n)
break;
cnt=cnt*prm[k];//累乘到当前数;
find(k+1,cnt,sum*(i+1)); //继续下一步搜索
}
}
int main()
{
while(scanf("%lld",&n)!=EOF)
{
ans=inf;
num=0;
find(0,1,1);
printf("%lld\n",ans);
}
return 0;
}
小明系列故事——未知剩余系
Time Limit: 500/200 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 912 Accepted Submission(s): 215
Problem Description
“今有物不知其数,三三数之有二,五五数之有三,七七数之有二,问物几何?”
这个简单的谜题就是中国剩余定理的来历。
在艰难地弄懂了这个定理之后,小明开始设计一些复杂的同余方程组X mod ai = bi 来调戏别人,结果是必然的,都失败了。
可是在这个过程中,小明发现有时并不一定要把ai和bi告诉你。他只需要告诉你,ai在区间 [1, X] 范围内每个值取一次时,有K个ai使bi等于0,或有K个ai使bi不等于0,最小的X就可以求出来了。
你来试试看吧!
Input
输入第一行为T,表示有T组测试数据。
每组数据包含两个整数Type和K,表示小明给出的条件。Type为0表示“有K个ai使bi等于0”,为1表示“有K个ai使bi不等于0”。
[Technical Specification]
1. 1 <= T <= 477
2. 1 <= K <= 47777, Type = 0 | 1
Output
对每组数据,先输出为第几组数据,如果没有这样的数,输出“Illegal”,否则输出满足条件的最小的X,如果答案大于2^62, 则输出“INF”。
Sample Input
30 31 30 10
Sample Output
Case 1: 4Case 2: 5Case 3: 48
#include<stdio.h>
#define N 50000
typedef long long ll;
#define inf ((ll)1<<62)+1//注意这里的书写形式,写成((ll)(1<<62))+1是不对的
int ip[N+10];
ll ans;
int prm[20]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
void isprm()
{
int i,j,k;
for(i=1;i<=N;i++) ip[i]=i; //初始化i最多有i个与其互质的数
for(i=1;i<=N;i++)
{
for(j=i;j<=N;j+=i)
ip[j]--; //i为j的因子,所以j与i不互质,第j个数的互质数减少1
k=ip[i];//此时ip[i]存的是不大于i的数与i互质的个数k=ip[i]
//如果ip[k]=0,表示小于i的所有数中,没有刚好有k个互质数的数
//故将ip[k]=i,表示刚好有k个与i互质的数个数最小为i
if(!ip[k])
ip[k]=i;
ip[i]=0;//标记“刚好有k个互质数的数”没有(不大于i的数不可能存在某个数与其互质的数的个数等于i);
}
}
void find(int k,ll cnt,int sum,int n)
{
int i;
if(sum>n) return;//剪枝
if(k>=15) return;//剪枝
if(sum==n&&ans>cnt) ans=cnt;
for(i=1;i<=62;i++)
{//注意这里不可以写成ans<cnt*prm[k];因为两个数相乘可能会超出long long的范围
if(ans/prm[k]<cnt||sum*(i+1)>n)//剪枝
break;
cnt=cnt*prm[k];
if(n%(sum*(i+1))==0)//剪枝,不然超时
find(k+1,cnt,sum*(i+1),n);
}
}
int main()
{
int t,aa=1,op,k;
scanf("%d",&t);
isprm();
while(t--)
{
scanf("%d %d",&op,&k);
printf("Case %d: ",aa++);
if(op==1)
{
ans=ip[k];
}
else
{
ans=inf;
find(0,1,1,k);
}
if(ans==0)
printf("Illegal\n");
else if(ans>=inf)
printf("INF\n");
else
printf("%I64d\n",ans);//用%I64d对了,%lld答案错误
}
return 0;
}