[小米OJ]不一样的排序


原题

在这里插入图片描述
5个月前的我比现在还要菜(废话),当时为了求一个数的因子个数我只会用o(n)的方法(一个一个试除,菜鸟方法)。也没有注意到这一题的范围是1e6((水平不够,关注点少)范围1e6可以联想到均摊时间,因为这一题的实际数字个数在1e7以下,1e6以下数字算出因子个数,1e7个数每个就只有索引时间复杂度o(1))以下,不过以当时的水平即便是注意到1e6也没用,以o(n)的时间复杂度,要算1e6以下的那要时间上o(1e12)(计算机1s的计算只有o(1e8)到o(1e9)), 明显要算到明年去。。。后来这几天看到单个数的质因数分解时间复杂度最低也o( n \sqrt n n )
按照如下公式:
在这里插入图片描述
来求因子个数时间复杂度最低也不会比o( n \sqrt n n )低。
要计算1e6以下的所有数因子个数勉勉强强也o(n* n \sqrt n n ) = o(1e9)可能还是会超时,于是此时就要均摊复杂度了。
可以联想到欧拉筛法o(n)。欧拉筛法的最大特征是不会重复筛,那我们是不是可以试一下在欧拉筛的过程中顺便也把每个数的因子个数算出来呢?
答案是完全可以,欧拉筛是用已筛出来的素数来筛新素数,新的数无论是素数还是合数,都是原有的数的某个素因子的幂加1后形成的数,于是可以利用原来的数算出的因子个数求新数的因子个数d,但前提是要知道这个原来的数里的这个素因子的幂,然后利用以上的公式,以数组divin[i]来记录数字i的因子个数(边界条件若i是素数,divin[i]肯定等于2),假设新值是val
那么根据以上公式得到:

divin[val] = divin[i] / (d + 1) * (d + 2)

注意到计算原数里某素因子的时间复杂度为o(logn),于是在欧拉筛法里嵌入这个就可以边筛素数边算当前数的因子个数,时间复杂度为o(nlogn)
divin数组初始化完成后把题目输入的数字和该数字的因子个数打包成结构体,然后按题意重载’<’,再利用STL里函数nth_element求第K大即可
于是终于可以AC这道题了(题目上的难度只是一般,我太蔡了TvT)

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 2;
const int maxd = 1e7 + 2;
bool valid[maxn];
int ans[maxn];
int divin[maxn];
int tot;

inline int factors(int n, int m)
{
	int cnt = 0;
	while (!(n % m))
	{
		cnt++;
		n /= m;
	}
	return cnt;
}

void getPrime(int n, int &tot, int ans[maxn])
{
	memset(valid, true, sizeof(valid));
	divin[1] = divin[0] = 1;
	for (int i = 2; i <= n; ++i)
	{
		if (valid[i])
		{
			tot++;
			ans[tot] = i;
			divin[i] = 2;
		}
		for (int j = 1; j <= tot && i * ans[j] <= n; ++j)
		{
			int val = i * ans[j];
			int d = factors(i, ans[j]) + 1;
			valid[val] = false;
			divin[val] = divin[i] / d * (d + 1);
			if (i % ans[j] == 0) break;
		}
	}
}

struct NUM
{
	int val, cnt;
	
	bool operator < (const NUM &a) const
	{
		return cnt < a.cnt || (cnt == a.cnt && val < a.val);
	}
} num[maxd];

int main()
{
	int K, n;
	getPrime(1e6, tot, ans);
	scanf("%d %d", &K, &n);
	for (int i = 0; i < n; ++i)
	{
		scanf("%d", &num[i].val);
		num[i].cnt = divin[num[i].val];
	}
	nth_element(num, num + K - 1, num + n);
	printf("%d\n", num[K - 1].val);
	return 0;
}

貌似是一道简单题目被我做复杂了,我还是不懂得变通啊。。。
给出一个在别处看到的同样是o(nlogn)的n以下求因子个数的算法(通过枚举因子来枚举数字)

void init(int n)
{
    int x = sqrt(n);
    divin[1] = divin[0] = 1;
    for (int i = 1; i <= x; i++){
        for (int j = i + 1; j * i <= n; j++) divin[i * j] += 2;
        divin[i * i]++;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值