NOIP普及组蒟蒻挣扎之模拟赛C组 第四题 ——约数国の王♂

版权声明:随便抄啦-严小猴 https://blog.csdn.net/qq_40170282/article/details/83212835

转自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 = A 1 P 1 A 2 P 2 A 3 P 3 A k P k x = A_1 ^ {P_1} * A_2^ {P_2} *A_3^ {P_3} * ……A_k^ {P_k}

也就是说我们只要求这个,就是 x x 的约数个数:(全排列问题嘛, p [ i ] p[i] 种情况还有 k k 它本身)

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

然后显而易见,我们就能看出 一个很简单的式子

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

申明一个F[ ]数组,这个数组用于储存 i i 个质因子的数中最小是谁,然后我们可知这样的数组是一定存在且可被求出的。
此时提到了一个 f f 数组,又是用来干嘛呢?
其实这个数组是用来储存选择了 k k 个前质因数(如果不是前几个不一定最大,有想法可以自己证明),有i个约数的数

巴拉拉魔法能量,敲黑板

f [ k + 1 ] [ i + 1 ] = m i n ( f [ k ] [ i ] p r i m e [ k + 1 ] j 0 &lt; j ) f[k + 1][i + 1] = min(f[k][i] * prime[k + 1] ^j|0 &lt; j \leq \infty)

此处的 p r i m e [ 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.

猜你喜欢

转载自blog.csdn.net/qq_40170282/article/details/83212835