Codeforces Round 903 (Div. 3)D题
题目
输入输出范例:
输入:
7
5
100 2 50 10 1
3
1 1 1
4
8 2 4 2
4
30 50 27 20
2
75 40
2
4 4
3
2 3 1
输出:
YES
YES
NO
YES
NO
YES
NO
题意解析
首先这道题你要看明白这个数组中任意两个元素其中的任意一个都是可以把自己的一个因子给奉献出去给另一个的,所以本质就是因子之间的转移,一直到每一个数的因子数目和种类全部一模一样的吻合才行。
这样一来,就不需要考虑因子交换的过程,只需要知道,只要每一种因子的数目都可以被n整除,那么就可以把因子分配平均(意味着让每一个数字相等)
方便来看,我们把所有数的所有质因子放一起全部统计种类和数目,只要出现的每一种质因子都可以被n整除,那么输出Yes反之输出No
代码实现
好吧,这个代码还是jiangly的,我承认我是jiangly的迷弟一枚
#include <bits/stdc++.h>
using i64 = long long;
std::vector<int> minp, primes;
void sieve(int n) {
minp.assign(n + 1, 0);
primes.clear();
for (int i = 2; i <= n; i++) {
if (minp[i] == 0) {
minp[i] = i;
primes.push_back(i);
}
for (auto p : primes) {
if (i * p > n) {
break;
}
minp[i * p] = p;
if (p == minp[i]) {
break;
}
}
}
}
constexpr int V = 1E6;
int cnt[V + 1];
void solve() {
int n;
std::cin >> n;
std::vector<int> stk;
for (int i = 0; i < n; i++) {
int x;
std::cin >> x;
while (x > 1) {
int p = minp[x];
x /= p;
stk.push_back(p);
cnt[p]++;
}
}
bool ok = true;
for (auto x : stk) {
if (cnt[x] % n) {
ok = false;
}
cnt[x] = 0;
}
std::cout << (ok ? "YES" : "NO") << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
sieve(V);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
然后进行部分代码精析
std::vector<int> minp, primes;
void sieve(int n) {
minp.assign(n + 1, 0);
primes.clear();
for (int i = 2; i <= n; i++) {
if (minp[i] == 0) {
minp[i] = i;
primes.push_back(i);
}
for (auto p : primes) {
if (i * p > n) {
break;
}
minp[i * p] = p;
if (p == minp[i]) {
break;
}
}
}
}
这是个板子,只要根据题的情况把一个足够大的数字n输入函数形参列表,那么你就可以,唔,我还没研究透这个板子,反正到时候minp[i]存储的应该是i的最小质因子,也许是吧?明天再细研究,有点懒了。
for (int i = 0; i < n; i++) {
int x;
std::cin >> x;
while (x > 1) {
int p = minp[x];
x /= p;
stk.push_back(p);
cnt[p]++;
}
}
这一个for循环里套while循环,把这个数组里的每一个元素的每一种质因子全部记录,不同元素有同一种的话就在数量上加
bool ok = true;
for (auto x : stk) {
if (cnt[x] % n) {
ok = false;
}
cnt[x] = 0;
}
std::cout << (ok ? "YES" : "NO") << "\n";
最后判断是否出现过的所有种类的质因子在数量上都可以均分到n个数里,由此一来可得出结果
欧拉筛板子
这个板子蛮有用的:
原理是遍历到i,然后就能找到小于等于i的所有数字的最小质因子,用前面已经找出来的所有小于等于i的质数
p
1
,
p
2
,
…
,
p
j
挨个乘以
i
,
得到
k
(
i
,
j
)
=
i
×
p
j
,
则可知
k
(
i
,
j
)
的最小质因子一定为
p
j
p_1 , p_2,\dots,p_j挨个乘以i,得到k_{(i,j)}=i\times p_j,则可知k_{(i,j)}的最小质因子一定为p_j
p1,p2,…,pj挨个乘以i,得到k(i,j)=i×pj,则可知k(i,j)的最小质因子一定为pj
由此原理,根据每一个 i ,可以得到所有能通过 i 和已知的所有质数能够产生出来的合数,并记录他们的最小质因子是谁。进而可以推出,如果遍历到某一个 i 他从来没有被产生出来过,它必然是质数。
然后时间复杂度是O(n),cool, 在此处不多做证明了,有疑问的可以点我下面的那个链接
std::vector<int> minp, primes;
void sieve(int n) {
minp.assign(n + 1, 0);
primes.clear();
for (int i = 2; i <= n; i++) {
if (minp[i] == 0) {
minp[i] = i;
primes.push_back(i);
}
for (auto p : primes) {
if (i * p > n) {
break;
}
minp[i * p] = p;
if (p == minp[i]) {
break;
}
}
}
}
唔,哪里有用了?明天再说
——————————————————手动分割线(doge)————————————————————
long long ago 写了这篇文章,今天无意间看到好像还有点没写完的东西,咳咳,于是丰富了一下我过去的懒惰,又在网上找了份说欧拉筛说的比较好的文章,欧拉筛,来进一步详细解释这个代码,没有看懂我代码上面讲的可以看看这篇文章,咳咳,代码大同小异,意思大差不离,大家凑活看。