思路:
设n个数,它们的最大公因子为G,最小公倍数为L,问有多少种不同的方法还原这个数列。
质因数分解:
每个数ai都可以分解为质因数的乘积,即:
ai=p1ei1⋅p2ei2⋅…⋅pkeik,
其中p1,p2,…,pk是质数,eij是质数pj在ai中的指数。
最大公因子G是所有ai共有的质因数的最小指数的乘积,
最小公倍数L是所有ai中每个质因数的最大指数的乘积。
可以知道,L一定是G的倍数。
每个质因数都是独立的,所以可以算出每个质因数在n个位置的填写情况,然后将每个质因数情况相乘。
对于质因数p,设在最大公因子中相乘l次,在最小公倍数中相乘r次,那么每个数中,相乘次数需满足[l, r],共有cnt = r - l + 1种情况。
不考虑重叠,n个位置,有cntn中方法,
注意:在n个数中需至少出现一次p相乘l次,至少出现一次p相乘r次,才能得到G和L。
在总情况中,减去没出现过l,减去没出现r的情况,再加上l和r都没出现过的情况,得到:
cntn - (cnt-1)n - (cnt - 1)n + (cnt -2)n
其实可以发现,我们只关心cnt的大小,所以令z = y / x,计算z的每个质因子相乘的个数即可。
注意事项:
在计算结果,减的过程中会出现负的情况,加mod后再减。
参考代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 998244353;
ll ksm(ll a, ll b) {
ll res = 1;
while(b) {
if(b & 1) res = a * res % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
void solve() {
ll x, y, n;
cin >> x >> y >> n;
if(y % x) {
cout << 0 << endl;
return;
}
y /= x;
vector<ll> v;
int t = 0;
for(int i = 2; i <= sqrt(y) && y != 1; i++) {
if(y % i) {
continue;
}
while(y % i == 0) {
t++;
y /= i;
}
if(t) v.push_back(t);
t = 0;
}
if(y != 1) v.push_back(1);
ll ans = 1;
for(auto e:v) {
ll t = ksm(e + 1, n);
if(t > 1) {
t = (t - ksm(e, n) + mod) % mod;
t = (t - ksm(e, n) + mod) % mod;
t = (t + ksm(e - 1, n)) % mod;
}
ans = ans * t % mod;
}
cout << ans << endl;
}
int main() {
int T;
cin >> T;
while(T--) solve();
return 0;
}0.0分
1 人评分
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程
发表评论 取消回复