目录
A 反素数加强版
时空限制
1s,32MB
问题描述
如果一个大于等于1的正整数n,满足所有小于n且大于等于1的所有正整数的约数个数都小于n的约数个数,则n是一个反素数。请你计算不大于n的最大反素数。
输入格式
第一行输入数据组数T,每组数据输入1个正整数n。
输出格式
对每组数据,输出不大于n的最大反素数。
数据范围
1<=T<=100,1<=n<=1e18。
思路
由数据范围可得时间复杂度为O(T+log(n))。
对于n=p1^r1*p2^r2*...*pk^rk(p为质数&&r>=1)的约数个数为(r1+1)*(r2+1)*...*(rk+1)。
又因为前15个质数之积大于1e18,所以只用dfs枚举前15个质数头上的指数,并将它们组合起来即可。
优化(剪枝):对于题目要求的n,rx>=ry(x<=y),即更小质数头上的指数一定小于等于更大质数头上的指数。感性理解,如果我在3的指数上加一,为什么不在2头上加一个呢,这样ans一样而且目前的数还要小一点,后面添加的东西还会更多一点。
代码
我加了个__int128,但是可以通过tp<n/p[num]来实现。
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#define int __int128
using namespace std;
long long t,n;
long long p[]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
struct node{
int cnt1,ans1;
}ans;
void dfs(long long num,long long r,int cnt,int sum,long long lr)
{
if(r>lr)return;
__int128 tp=sum*(long long)pow(p[num],r);
if(tp*p[num]>n)
{
if(ans.cnt1<cnt*(r+1)||((ans.cnt1==cnt*(r+1))&&(ans.ans1>tp)))
{
ans.cnt1=cnt*(r+1),ans.ans1=tp;
}
}
if(tp*p[num]<=n)
{
dfs(num,r+1,cnt,sum,lr);
}
if(tp*p[num+1]<=n&&num<=14)
{
dfs(num+1,0,cnt*(r+1),tp,r);
}
}
void w(__int128 x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)w(x/10);
putchar(x%10+'0');
}
signed main()
{
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
dfs(1,0,1,1,100);
w(ans.ans1);
printf("\n");
ans.ans1=0,ans.cnt1=0;
}
return 0;
}
注释:num:现在枚举到第几个质数。
r:现在枚举到的质数头上的指数。
cnt:除开现在枚举到的指数外,总数的因数个数。
sum:除开现在枚举到的指数外的总数。
lr:上一个质数的指数。
B 约数积函数
时空限制
1s,256MB
问题描述
约数积函数(n),表示n的所有约数的乘积。
输入格式
第一行输入数据组数T,对每组数据:
输入一个整数 n。
输出格式
对每组数据,输出一个整数,表示(n)对1e9+7取模。
数据范围
1<=T<=1e5,1<=n<=1e7 。
思路
看数据可知时间复杂度为O(n+T)。
因为约数是俩俩成对的,所以我们只需要算出它有多少对约数即可。然后判断它是不是完全平方数。 很6呀!
问题是怎么算约数个数?
这就要请出R1的关键算法欧拉筛(线性筛)了。
欧拉筛模板:
void ol(int N)
{
for(int i=2;i<N;i++)
{
if(f[i]==0)p[++idx]=i;
for(int j=1;j<=idx&&i*p[j]<N;j++)
{
f[i*p[j]]=1;
if(i%p[j]==0)break;
}
}
}
它之所以是线性的,是因为每个数只被筛到一次且只会被它的最小质因子筛到。
所以可以将枚举的i分为两类,一类是已经有p[j]这个质因子(if(i%p[j]==0)),一类没有(else)。
我们设r[i]表示i最小质因子的指数,d[i]表示i的因数个数。
可推得:
当i%p[j]==0时:
r[i*p[j]]=r[i]+1,d[i*p[j]]=d[i]/(r[i]+1)*(r[i]+2)
分析(p[j]即为p1