【无码专区6】球与盒子(数学线性筛)

因为只有std,没有自我实现,所以是无码专区

主要是为了训练思维能力

solution才是dls正解,但是因为只有潦草几句,所以大部分会有我自己基于正解上面的算法实现过程,可能选择的算法跟std中dls的实现不太一样。

std可能也会带有博主自己的注释。

problem

n n n 个球放入 n n n 个盒子里面,每个盒子里恰好都有一个球,标号从 1 1 1 开始。

且盒子标号和里面装的球的标号的因子个数必须相同。

答案对 500009 500009 500009 取模。多组数据。

T ≤ 1 e 5 , n ≤ 1 e 9 T\le 1e5,n\le 1e9 T1e5,n1e9


我的想法

observation :因子数不同的之间互相独立,因子数相同的之间方案数为阶乘,所以实际上是不同因子数的方案数的乘积。

对于 70 % 70\% 70% n ≤ 1 e 6 n\le 1e6 n1e6 数据,可以线性筛预处理将所有数按照因子个数分类,然后计算即可。

多组数据肯定不能每次都 O ( n ) O(n) O(n) 的扫一遍。

将查询的 n i n_i ni 按升序排列,指针扫的时候将 i i i 加入,就单独处理 i i i 因子个数那个集合的贡献,很好维护。

observation 1 1 1 只能放在 1 1 1 里。

observation:质数之间可以互相放。

由整数的唯一标准分解,所有数都可以被若干个质数的幂表示。


solution

观察到,模数是非常小的,不同于往常的大质数。

所以当 c n t x ≥ m o d cnt_x\ge mod cntxmod 之后,答案一定为 0 0 0

c n t x : cnt_x: cntx: 因子数为 x x x 的个数。

最后答案为 ∏ c n t x ! \prod cnt_x! cntx!

通过打表发现,当 n ≥ 2250000 n\ge 2250000 n2250000,就已经存在一个 c n t x ≥ m o d cnt_x\ge mod cntxmod 了。

所以只需要在 n < 2250000 n<2250000 n<2250000 时用线性筛求出每个数的因子数,乘上对应的 c n t x cnt_x cntx,当 n ≥ 2250000 n\ge 2250000 n2250000 直接输出 0 0 0 即可。

一般而言,当模数不是寻常的模数时,正解往往会隐藏在模数的性质背后。

当模数是常见的大质数,一般都是数学计算方案了。


std

#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <cassert>
#include <ctime>
#include <queue>
using namespace std;
#define VAR(a,b) __typeof(b) a=(b)
#define REP(i,n) for(int _n=n, i=0;i<_n;++i)
#define FOR(i,a,b) for(int i=(a),_b=(b);i<=_b;++i)
#define FORD(i,a,b) for(int i=(a),_b=(b);i>=_b;--i)
#define FOREACH(it,c) for(VAR(it,(c).begin());it!=(c).end();++it)
#define ALL(c) (c).begin(),(c).end()
#define TRACE(x) cerr << "TRACE(" #x ")" << endl;
#define DEBUG(x) cerr << #x << " = " << x << endl;

typedef long long LL;
typedef unsigned long long ULL;
const int INF = 1000000000;
const LL INFLL = LL(INF) * LL(INF);
template<class T> inline int size(const T &c) {
	return c.size();
}
int rint() {
	int x;
	if (scanf("%d", &x) != 1)
		return -1;
	return x;
}
LL rLL() {
	LL x;
	if (scanf("%lld", &x) != 1)
		return -1;
	return x;
}

const int MOD = 500009;
const int MAXN = 2250000;

int main() {
	freopen("ball.in", "r", stdin);
	freopen("ball.out", "w", stdout);
	vector<int> ndivisors(MAXN + 1, 1);
	int p = 2;
	for (p = 2; p * p * p <= MAXN; ++p)
		if (ndivisors[p] == 1) {
			int pk = p;
			for (int k = 1;; ++k) {
				int mm = 0;
				for (int q = pk; q <= MAXN; q += pk) {
					++mm;
					if (mm == p)
						mm = 0;
					else
						ndivisors[q] *= k + 1;
				}
				LL pk2 = LL(pk) * p;
				if (pk2 > MAXN)
					break;
				pk = int(pk2);
			}
		}
	for (; p * p <= MAXN; ++p)
		if (ndivisors[p] == 1) {
			int pk = p;
			for (int k = 1; k <= 2; ++k) {
				int mm = 0;
				for (int q = pk; q <= MAXN; q += pk) {
					++mm;
					if (mm == p)
						mm = 0;
					else
						ndivisors[q] *= k + 1;
				}
				pk *= p;
			}
		}
	for (; p <= MAXN; ++p)
		if (ndivisors[p] == 1) {
			for (int q = p; q <= MAXN; q += p) {
				ndivisors[q] *= 2;
			}
		}
	vector<unsigned> cnt(MAXN + 1, 0);
	vector<unsigned> res(MAXN + 1, 0);
	res[0] = 1;
	for (int n = 1; n <= MAXN; ++n) {
		int d = ndivisors[n];
		++cnt[d];
		res[n] = res[n - 1] * (cnt[d] % 1024u) % unsigned(MOD);
		if (cnt[d] >= 1024u) {
			unsigned a = res[n - 1] * (cnt[d] >> 10) % unsigned(MOD);
			res[n] = (res[n] + (a << 10)) % unsigned(MOD);
		}
		if (res[n] == 0)
			break;
	}
	int ntc = rint();
	REP(tc, ntc) {
		int n = rint();
		int r = n <= MAXN ? res[n] : 0;
		printf("%d\n", r);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值