1. 程式人生 > >NOIP普及組蒟蒻掙扎之模擬賽C組 第四題 ——約數國の王♂

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秀的方法

正解在此

首先我們可以知道任意一個整數都可以理解為多個素數的乘積(咀嚼一下)
辣麼,我們可以得到如下公式

x=A1P1A2P2A3P3AkPkx = A_1 ^ {P_1} * A_2^ {P_2} *A_3^ {P_3} * ……A_k^ {P_k}

也就是說我們只要求這個,就是xx的約數個數:(全排列問題嘛,p[i]p[i]種情況還有kk它本身)

ki=1(Pi+1)\prod \frac{k}{i=1} (P_i + 1)

然後顯而易見,我們就能看出 一個很簡單的式子

F[i]=min(f[k][i]0&lt;k)F[i] = min(f[k][i] | 0 &lt; k \leq \infty)

申明一個F[ ]陣列,這個陣列用於儲存ii個質因子的數中最小是誰,然後我們可知這樣的陣列是一定存在且可被求出的。
此時提到了一個ff陣列,又是用來幹嘛呢?
其實這個陣列是用來儲存選擇了kk個前質因數(如果不是前幾個不一定最大,有想法可以自己證明),有i個約數的數

巴拉拉魔法能量,敲黑板

f[k+1][i+1]=min(f[k][i]prime[k+1]j0&lt;j)f[k + 1][i + 1] = min(f[k][i] * prime[k + 1] ^j|0 &lt; j \leq \infty)

此處的prime[i]prime[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.