题解:P7360 「JZOI-1」红包
j23wuyifan · · 题解
前言:
好题啊,可惜各位大佬的莫反,容斥,一堆的式子我都看不懂啊,只看懂了 Corzica 大佬的做法,这里主要是对其做法的补充解释。
思路:
首先考虑对于每个质数
又因为读入的
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
const int mod = 998244353;
const int phi = 402653184;//998244352 的phi值
int n, m, pr[N], cnt, f[N], T;
bool vis[N];
//快速幂
inline int quickpow(int x, int y, int mod) {
if (y == 1)return x;
if (y == 0)return 1;
int g = quickpow(x, y / 2, mod);
if (y & 1)return g * g % mod * x % mod;
return g * g % mod;
}
signed main() {
ios :: sync_with_stdio(0);
cin.tie(0), cout.tie(0);
//f[i]记录i这个数是不是一个质数的 n 次方,是的话,是哪个质数,可以线性筛预处理
n = 1e6;
for (int i = 2; i <= n; i ++) {
if (!vis[i]) {
pr[++ cnt] = i;
f[i] = i;
}
for (int j = 1; j <= cnt && i * pr[j] <= n; j ++) {
vis[pr[j] * i] = 1;
if (i % pr[j] == 0) {
f[i * pr[j]] = f[i];
break;
}
}
}
f[0] = 1;
for (int i = 1; i <= n; i ++) {
if (!f[i])f[i] = 1;
f[i] = f[i - 1] * f[i] % mod;
}
//预处理前缀积
cin >> T;
while (T --) {
cin >> n;
string s;
cin >> s;
m = 0;
for (int i = 0; i < s.size(); i ++) {
m = m * 10 + s[i] - '0';
if (m >= phi) m = m % phi + phi;
}//读入时将k 模 φ(mod - 1),因为 k 在最后的式子上在指数的指数上
int all = quickpow(n, m, mod - 1), ans = 1;
//总是用 n ^ k 去减,可以直接算好
for (int l = 1; l <= n; l ++) {
int r = n / (n / l);//整除分块
if (f[r] != f[l - 1]) {
//f[r] / f[l - 1] 求 l ~ r 的积
ans *= quickpow(f[r] * quickpow(f[l - 1], mod - 2, mod) % mod, (all - quickpow(n - n / l, m, mod - 1)/*减去 lcm 中没有 p ^ q 的方案*/ + mod - 1) % (mod - 1), mod);
ans %= mod;
}
l = r;
}
cout << ans << '\n';
}
return 0;
}