NOIP普及組蒟蒻掙扎之模擬賽C組 第四題 ——約數國の王♂
轉自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.