转自jzoj & c渣渣党福利
题目描述
数学的王国里,有一些约数国王……约数国王的定义是这样的:一个大于1的整数n,如果它约数的个数比1~n-1的每个整数的约数的个数都要多,那么我们就称它为约数国王。聪明的(______)在奥数书上认识了它们,于是产生了一个问题:他想知道L到R之间一共有多少个约数国王?它们分别又是谁?
输入
输入文件只有一行,包含一个l,一个r,表示小明想知道的范围。
输出
只有一行,第一个数h,表示l~r内一共有多少个约数国王,接下来h个从小到大的数(为了防止国王们打架,你需要按顺序输出。),表示约数国王分别是谁。
样例输入
1 100
样例输出
8 2 4 6 12 24 36 48 60
数据范围限制
对于30%的数据,1<=l<=r<=200000。
对于50%的数据,1<=l<=r<=500000。
对于70%的数据,保证最大的约数国王的约数的个数不大于1000。
对于100%的数据,1<=l<=r, 并且保证l,r在64位整型以内,最大的约数国王的约数的个数不大于200000。
题解 & 思路
首先很肯定的 是暴力:
#include<bits/stdc++.h>
using namespace std;
int biao[10][10] = {{0,0},{0,1},{0,2,4,8,6},{0,3,9,7,1},{0,4,6},{5,5},{6,6},{0,7,9,3,1},{0,8,4,2,6},{0,9,1}};
//手动打表模拟各位次方
int len[10] = {1,1,4,4,2,1,1,4,4,2};
int t,ans,n,a,b;
int main()
{
//freopen("superpow.in","r",stdin);
//freopen("superpow.out","w",stdout);
scanf("%d",&t);
while (t--)
{
ans = 1;
scanf("%d",&n);
for (int i = 1;i <= n;i++)
{
scanf("%d%d",&a,&b);
int x = a;
while (--b)
{
if (x == len[a]) x = biao[a][x];
else x = biao[a][x % len[a]];
}
ans *= x;
ans %= 10;
}
printf("%d\n",ans);
}
return 0;
}
所以说 这是一个U秀的方法
正解在此
首先我们可以知道任意一个整数都可以理解为多个素数的乘积(咀嚼一下)
辣么,我们可以得到如下公式
也就是说我们只要求这个,就是 的约数个数:(全排列问题嘛, 种情况还有 它本身)
然后显而易见,我们就能看出 一个很简单的式子
申明一个F[ ]数组,这个数组用于储存 个质因子的数中最小是谁,然后我们可知这样的数组是一定存在且可被求出的。
此时提到了一个 数组,又是用来干嘛呢?
其实这个数组是用来储存选择了 个前质因数(如果不是前几个不一定最大,有想法可以自己证明),有i个约数的数的
巴拉拉魔法能量,敲黑板
此处的 是第i个素数(抱歉,要自己打,想用现成的函数门都没得)——筛吧筛吧我的骄傲放纵
int isPrime(int n)//julao倾情贡献尊享暴力筛代码
{
float n_sqrt;
if(n <= 3) return 1;
if(n%6!=1 && n%6!=5) return 0;//julao菠萝蜜之优化
n_sqrt=floor(sqrt((float)n));
for(int i=5;i<=n_sqrt;i+=6) if(n%(i)==0 || n%(i+2)==0) return 0;
return 1;
}
有了蜜汁筛的强力加持,我们的程序便呼之欲出了——
呵呵,依然不是程序:
const//学自大佬代码,c渣渣坏了,Cp顶替一下吧
maxn=161281;
maxint=9223372036854775807;
zs:array[1..31]of longint=
(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,57,61,67,71,73,79,83,89,97,101,103,107,109,113,127);
var
f,q:array[1..13,1..170000]of qword;
l,r,sum,min:int64;
c,b:array[1..170000]of qword;
procedure dp;
var i,j,k,o:longint; p:qword;
begin
for i:=1 to 13 do
for j:=1 to 170000 do
f[i,j]:=maxint;
f[1,1]:=1;
f[1,2]:=2;
for i:=3 to 63 do
f[1,i]:=f[1,i-1]*2;
fillchar(q,sizeof(q),1);
for i:=1 to 12 do
for j:=1 to 81000 do
begin
p:=1;
if q[i,j]>maxn div j then o:=maxn div j
else o:=q[i,j];
for k:=1 to o do
begin
if maxint div zs[i+1]<p then break;
p:=p*zs[i+1];
if maxint div p<f[i,j] then break;
if f[i,j]*p<f[i+1,j*(k+1)] then begin
f[i+1,j*(k+1)]:=f[i,j]*p;
q[i+1,j*(k+1)]:=k;
end;
end;
end;
end;
procedure main;
var i,j:longint;
begin
readln(l,r);
dp;
fillchar(c,sizeof(c),$7);
b[maxn+1]:=maxint;
for i:=maxn downto 1 do
begin
min:=maxint;
for j:=1 to 13 do
if f[j,i]<min then min:=f[j,i];
c[i]:=min;
if c[i]<b[i+1] then b[i]:=c[i] else b[i]:=b[i+1];
end;
for i:=2 to maxn do
if (c[i]<b[i+1])and(c[i]>=l)and(c[i]<=r) then inc(sum);
end;
procedure print;
var i,j:longint;
begin
for i:=2 to maxn do
if (c[i]>=l)and(c[i]<=r)and(c[i]<b[i+1]) then write(c[i],' ');
end;
begin
assign(input,'king.in');reset(input);
assign(output,'king.out');rewrite(output);
main;
write(sum,' ');
print;
close(input); close(output);
end.