安吉D19 T1
- 1安吉D19 T1本文
- 2安吉D19 T3
- 3安吉D20 模考12总结
- 4安吉D17 T3
- 5安吉D17 T4
- 6安吉D19 模考11总结
- 7安吉D15 A
- 8安吉D16 T4
- 9安吉D17 T1
- 10安吉D17 模考10总结
- 11安吉D16 T3
- 12安吉D16 模考9总结
- 13安吉D13 模考8总结
- 14安吉D13-T2
- 15安吉D13-T3
- 16安吉D12-T3
- 17安吉D11-B
- 18安吉D11-C
- 19安吉D11-D
- 20安吉D12 模考7总结
- 21安吉D12-T2
- 22安吉D11-A
- 23安吉D10 模考6总结
- 24安吉D10-T3
- 25安吉D10-T4
- 26安吉D8-E
- 27安吉D9-T4
- 28安吉D8-A
- 29安吉D8-B
- 30安吉D8-C
- 31安吉D8-D
- 32安吉D9 模考5总结
- 33安吉D9-T2
- 34安吉D9-T3
- 35安吉D6-T4
- 36安吉D4-G
- 37安吉D5-T2
- 38安吉D6 模考4总结
- 39安吉D6-T1
- 40安吉D6-T3
- 41安吉-开营测试 T4
- 42安吉D5 模考3总结
- 43安吉D5-T3
- 44安吉D4-A
- 45安吉D4-B
- 46安吉D4-C
- 47安吉D4:容斥原理
- 48安吉Day4-D
- 49安吉D3 模考2总结
- 50安吉D3-T1
- 51安吉D3-T2
- 52安吉D3-T4
- 53安吉D2 模考1总结
- 54安吉D2-T2
- 55安吉D1-G
- 56安吉D1-L
- 57安吉D2-T4
原题呈现
排列数
题目描述
排列数的符号为 ,意为从 个物品中任选 个,并按照任意顺序排成一列的方案数.和组合数不同,排列数与选出物品的顺序有关.
以琪露诺的智商当然学不明白排列数的含义,所以她把公式背了下来:
幸运的是,期末考试的压轴大题刚好考了排列数.题目是这样的: 求有多少对 ,满足 且 为 的倍数.
可惜只背公式当然是没什么用的,琪露诺完全不会这个题目,你能帮帮她吗?
输入格式
输入仅一行,包含两个整数 ,表示排列数的大小限制与倍数限制.
输出格式
输出仅一个整数,表示满足 且 为 的倍数的有序数对 的数量.
样例
输入 #1
10 1输出 #1
55输入 #2
10 6输出 #2
43输入 #3
100 66输出 #3
4540样例解释
-
样例一中,满足 的任意 都是 的倍数,共 个.
-
样例二中,符合范围条件但是不是 的倍数的排列数有:
共 个. 因此符合条件的为 个.
数据范围
| 数据包 | 分数 | 特殊性质 | ||
|---|---|---|---|---|
| 1 | 30 | 无 | ||
| 2 | 10 | 无 | ||
| 3 | 10 | 无 | ||
| 4 | 10 | 无 | ||
| 5 | 10 | 无 | ||
| 6 | 10 | A | ||
| 7 | 20 | 无 |
特殊性质 A:保证 为质数.
对于所有数据:
我们可以将排列数的公式变一下形,为:
那么只需要求所有乘积是 的倍数的区间数量.显然,若一个区间乘积已经是 的倍数了,后面不管加多少数,都是 的倍数.因此,对于所有增加一个数之后恰好可以满足条件的区间,其贡献是这个区间后面数的数量 +1,因为可以增加任意数量(可以为 0)的后面的数.(为什么不加前面的数?加上前面的数的区间会在前面被计算过一次,再增加一次会导致重复)、
具体的,使用双指针来维护.一个指针 指向当前区间的起点,一个指针 指向当前区间的终点.若当前区间积还不是 的倍数,那么尝试增加一个数满足限制,让指针 前进,直到满足要求.此时这个区间的贡献是区间后面还有多少不在区间内的数,再 +1.那么这样以 开头的区间全部统计完成了,让 前进一步.
还有一些问题:如何判断一个区间的区间积是 的倍数?
如果强行计算进行比较的话 __int128 都存不下,实际上,记录区间中所有数字乘积的分解质因数的结果,再和 分解之后的结果进行比较即可.
那问题又来了, 的值域限制是 ,这无法对 进行质因数分解,怎么办呢?实际上,有一个小观察:若 拥有一个质因数 满足 ,则答案为 0.这也很好理解,如果 拥有这样的 ,则无论如何,乘积都不可能有大于每个数本身的质因数.对 花费 的时间复杂度预处理一下即可.
实践中常常需要我们对于一个数进行分解质因数操作,这该如何实现呢?
分解质因数,首先需要知道质数.这里重点介绍线性筛质数这一种方法.
对于一个未确定的数,我们进行以下流程:
- 若这个数未被标记过,则这个数是质数.
- 接下来,无论这个数是否被标记过,都用它乘上所有已知的质数 ,设所得结果为 ,则将 标记.可以证明, 是 的最小质因数.
以下程序在记录质数的同时,记录了每一个数的最小质因数().
void calPrime() { for (int i = 2; i <= N; i++) { if (spf[i] == 0) { primes.push_back(i); } for (int j = 0; j < primes.size(); j++) { int pr = primes[j]; if (pr * i > N) break; spf[pr * i] = pr; } }}知道了每一个数的最小质因数之后,就可以快速对一个数分解质因数.
具体来说,当想要分解一个数 时,将其除以其最小质因数 ,这样在将 作为贡献之后,原问题就转化为了规模更小的子问题:分解 ,直到 时,就是该问题的边界,此时 也是质数(或 0),加入贡献.
以下程序在上面程序的基础上,计算了 的质因数:
typedef pair<int, int> pii;vector<pii> ans;while (true) { if (spf[x] == 0) { if (x != 1) ans.push_back({x, 1}); break; } int pr = spf[x]; pii rd = {pr, 0}; while (true) { if (x % pr) break; rd.second++; x /= pr; } ans.push_back(rd);}return ans;标程
#include <bits/stdc++.h>using namespace std;typedef long long ll;typedef pair<int, int> pii;const int N = 1e6 + 100;int n, k;ll ans;vector<int> primes;vector<pii> stdpr;int spf[N];unordered_map<int, int> um;
void calPrime() { for (int i = 2; i <= N - 10; i++) { if (spf[i] == 0) { primes.push_back(i); } for (int j = 0; j < primes.size(); j++) { int pr = primes[j]; if (pr * i > N - 10) break; spf[pr * i] = pr; } }}
vector<pii> calpr(int x) { vector<pii> ans; while (true) { if (spf[x] == 0) { if (x != 1) ans.push_back({x, 1}); break; } int pr = spf[x]; pii rd = {pr, 0}; while (true) { if (x % pr) break; rd.second++; x /= pr; } ans.push_back(rd); } return ans;}
bool isAllow() { for (int i = 0; i < stdpr.size(); i++) { int pr = stdpr[i].first; if (um[pr] < stdpr[i].second) { return false; } } return true;}
void insert(int x) { vector<pii> vec = calpr(x); for (int i = 0; i < vec.size(); i++) { int pr = vec[i].first; um[pr] += vec[i].second; }}
void remove(int x) { vector<pii> vec = calpr(x); for (int i = 0; i < vec.size(); i++) { int pr = vec[i].first; um[pr] -= vec[i].second; }}
signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); freopen("num.in", "r", stdin); freopen("num.out", "w", stdout); cin >> n >> k; calPrime(); if (k == 1) { cout << 1ll * (1 + n) * n / 2; return 0; } for (int i = 0; i < primes.size(); i++) { if (k % primes[i] == 0) { pii t = {primes[i], 0}; while (k % primes[i] == 0) { k /= primes[i]; t.second++; } if (t.second != 0) stdpr.push_back(t); } } if (k != 1) { cout << 0; return 0; } int i = 1, j = 1; while (i <= n) { if (isAllow()) { ans += 0ll + n - i + 1; remove(j); j++; if (i < j) { i++; insert(i); } } else { i++; insert(i); } } cout << ans;}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


