DZY Loves Math 系列详细题解
BZOJ 3309: DZY Loves Math I
题意
\(f(n)\) 为 \(n\) 幂指数的最大值。
\(T\le 10000, 1 \le a,b \le 10^7\)
题解
令 \(dx = T\) 那么有
我们线性筛出 \(f * \mu\) 就行了,每次整除分块回答,复杂度是 \(\mathcal O(n + T \sqrt n)\) 的。
什么不会线性筛?那么埃氏筛卡常吧。
BZOJ 3462: DZY Loves Math II
题意
\(2\le S \le 2*10^6,1 \le n \le 10^{18},1 \le q \le 10^5\)
题解
好神啊。。
我们考虑把相同的 \(p_i\) 合并,那么就变成
求第一个方程 \(c_i\) 解的方案数。那么对于 \(S\) 的唯一分解来说,不存在一个质因子的次数 \(>1\) 。
对于 \(c_i \ge 1\) 的限制我们可以把 \(n - \sum_i p_i\) 来去掉,那么现在我们只需要求一个完全背包的方案数。
对于一个因子 \(p_i\) 选 \(c_i\) 个的体积是 \(p_i c_i\) ,其实就等价于 \(xS + yp_i\) 这个方案。
什么意思呢?我们把 \(p_ic_i\) 对于 \(S\) 取模后就得到了 \(yp_i\) ,也就意味着 \(yp_i < S\) 。
那么我们要求的其实就是
的方案数。
考虑移项,那么就变成
不难发现对于前者的大小不会超过 \(kS\) ,可以直接暴力做多重背包就行了。
那么对于后面的方案如何算呢?不难发现其实就是求 \(\sum_{i \le k} x = \displaystyle \frac{n - \sum_{i \le k}y p_i}{S}\) 的方案数,直接隔板一下就行了。
复杂度其实是 \(\mathcal O(k^2S + qk^2)\) 的,跑的挺快。
总结
对于一类计数问题,可以考虑如何把原来模型等价替代,常常能优化复杂度,或减少难度。
代码
注意多重背包那里可以减掉不合法的优化一下复杂度。
#include <bits/stdc++.h>
#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
using namespace std;
typedef long long ll;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline ll read() {
ll x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("3462.in", "r", stdin);
freopen ("3462.out", "w", stdout);
#endif
}
const int N = 2e6 + 1e3, Mod = 1e9 + 7;
inline void add(int &a, int b) {
if ((a += b) >= Mod) a -= Mod;
}
inline int fpm(int x, int power) {
int res = 1;
for (; power; power >>= 1, x = 1ll * x * x % Mod)
if (power & 1) res = 1ll * res * x % Mod;
return res;
}
int fac[N], ifac[N];
void Fac_Init(int maxn) {
fac[0] = ifac[0] = 1;
For (i, 1, maxn) fac[i] = 1ll * fac[i - 1] * i % Mod;
ifac[maxn] = fpm(fac[maxn], Mod - 2);
Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1ll) % Mod;
}
inline int comb(ll n, int m) {
if (n < 0 || m < 0 || n < m) return 0;
int res = 1;
for (ll i = n; i >= n - m + 1; -- i)
res = 1ll * (i % Mod) * res % Mod;
return 1ll * res * ifac[m] % Mod;
}
int S, q, p[20], ptot, f[2][N * 7];
int main () {
File();
S = read(); int tmp = S; q = read();
for (int i = 2; i * i <= tmp; ++ i)
while (!(tmp % i)) tmp /= i, p[++ ptot] = i;
if (tmp > 1) p[++ ptot] = tmp;
sort(p + 1, p + ptot + 1);
ptot = unique(p + 1, p + ptot + 1) - p - 1;
if (p[ptot + 1]) {
while (q --) puts("0"); return 0;
}
int cur = 0; f[0][0] = 1;
For (i, 1, ptot) {
cur ^= 1; Cpy(f[cur], f[cur ^ 1]);
For (j, 1, i * S) {
if (j >= p[i]) add(f[cur][j], f[cur][j - p[i]]);
if (j >= S) add(f[cur][j], Mod - f[cur ^ 1][j - S]);
}
}
int sum = accumulate(p + 1, p + ptot + 1, 0); Fac_Init(ptot);
while (q --) {
ll n = read() - sum, ans = 0;
if (n < 0) {
puts("0"); continue;
}
for (int i = n % S; i <= ptot * S; i += S)
ans = (ans + 1ll * f[cur][i] * comb((n - i) / S + ptot - 1, ptot - 1)) % Mod;
printf ("%lld\n", ans);
}
return 0;
}
BZOJ 3481: DZY Loves Math III
题意
给定整数 \(P, Q\) 求满足方程 \(xy \equiv Q \pmod P\) 的整数解 \((x, y)\) 的数量,满足 \(0 \le x, y < P\) 。
其中 \(P = \prod_{i \le N} P_i, Q = \prod_{i \le N} Q_i\) 。
\(1 \le N \le 10, 0 \le Q_i \le 10^{18}, 1 \le P_i \le 10^{18},P \ge 2\)
题解
考虑枚举一个 \(x\) ,那么我们就是求对于 \(yx + kP = Q\) 的整数解个数。
利用在扩欧学习的知识,只有在 \(\gcd(x, P) | Q\) 的时候才会有解,且在这个模意义下有 \(\gcd(x, P)\) 个解。
那么答案其实就是
考虑枚举 \(\gcd(P, Q) = T\) 那么答案其实就是
如果你足够厉害,就能发现 \(f(T)\) 其实是个积性函数。
其实是因为积性函数和完全积性函数的狄利克雷卷积是个积性函数。。。(把 \(P\) 那里提出一个因子变成 \(T\) 就行了)
那么我们就能对于每个质因子 \(p\) 单独考虑啦,设它在 \(P\) 中有 \(q\) 个,\(T\) 中有 \(q'\) 个(显然有 \(q' \le q\) )。
然后贡献其实就是
但是这个会在 \(q' = q\) 的时候出现问题,因为 \(\varphi(1) = 1\) 那么我们就会把个 \(p^q\) 算成 \((p - 1)p^{q - 1}\) ,加回去即可。
总结
\(yx + kP = Q\) 有在 \(\gcd(x, P) | Q\) 的时候才会有解,且在这个模 \(P\) 意义下有 \(\gcd(x, P)\) 个解。
注意观察积性函数的性质,用质数去算答案大大降低复杂度。
代码
要个 \(\text{Pollard Rho}\) 分解质因数qwq
#include <bits/stdc++.h>
#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define pb push_back
using namespace std;
typedef long long ll;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline ll read() {
ll x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("3481.in", "r", stdin);
freopen ("3481.out", "w", stdout);
#endif
}
bool flag;
namespace Pollard_Rho {
inline ll rnd() { return abs((rand() << 30ll) | rand()); }
inline ll randint(ll l, ll r) { return rnd() % (r - l + 1) + l; }
const double eps = 1e-3;
inline ll mul(ll a, ll b, ll Mod) {
ll tmp = (a * b - (ll) ((long double) a / Mod * b + eps) * Mod);
return tmp < 0 ? tmp + Mod : tmp;
}
inline ll fpm(ll x, ll power, ll Mod) {
ll res = 1;
for(; power; power >>= 1, x = mul(x, x, Mod))
if (power & 1) res = mul(res, x, Mod);
return res;
}
const int times = 8;
inline bool Miller_Rabin(ll p) {
if (p <= 2) return p == 2;
if (!(p & 1)) return false;
ll u = p - 1; int power = 0;
for (; !(u & 1); u >>= 1) ++ power;
For (i, 1, times) {
ll a = randint(2, p - 1), x = fpm(a, u, p), y;
for (int i = 1; i <= power; ++ i, x = y) {
if ((y = mul(x, x, p)) == 1 && x != 1 && x != p - 1) return false;
}
if (x != 1) return false;
}
return true;
}
ll c, Mod;
ll f(ll x) { return (mul(x, x, Mod) + c) % Mod; }
ll find(ll x) {
if (!(x & 1)) return 2; Mod = x;
ll a = randint(2, x - 1), b = a; c = randint(2, x - 1);
do {
a = f(a); b = f(f(b));
ll p = __gcd(abs(a - b), x);
if (p > 1) return p;
} while (b != a);
return find(x);
}
void ReSolve(ll x, map<ll, int> &factor) {
if (x <= 1) { flag |= !x; return; }
if (Miller_Rabin(x)) { ++ factor[x]; return; }
ll fac = find(x); ReSolve(fac, factor); ReSolve(x / fac, factor);
}
}
map<ll, int> P, Q, T; typedef map<ll, int> :: iterator iter;
const int Mod = 1e9 + 7;
int main() {
File();
int n = read();
srand(998244353);
using Pollard_Rho :: ReSolve;
using Pollard_Rho :: fpm;
For (i, 1, n) ReSolve(read(), P);
For (i, 1, n) ReSolve(read(), Q);
if (flag) Q = P;
for (iter it = Q.begin(); it != Q.end(); ++ it) {
ll p = it -> first; T[p] = min(P[p], Q[p]);
}
int ans = 1;
for (iter it = P.begin(); it != P.end(); ++ it)
if (it -> second) {
ll p = it -> first, res;
res = (p - 1) % Mod * (T[p] + 1) % Mod * fpm(p, P[p] - 1, Mod) % Mod;
if (T[p] == P[p])
res = (res - (p - 1) % Mod * fpm(p, P[p] - 1, Mod) + fpm(p, P[p], Mod)) % Mod;
ans = 1ll * ans * (res + Mod) % Mod;
}
printf ("%d\n", ans);
return 0;
}
BZOJ 3512: DZY Loves Math IV
题意
给定 \(n, m\) 求
\(1 \le n \le 10^5, 1 \le m \le 10^9\)
题解
神题啊!
为啥 \(n, m\) 不是同级的?因为是让我们暴力枚举 \(n\) 。
那么我们求得就是
考虑如何将 \(\varphi(ni)\) 给拆开,因为 \(\varphi(x)\) 只有每个质因子 \(p\) 第一次出现的时候才会是 \(p - 1\) ,其他时候都直接乘 \(p\) 。
我们设 $n = \prod_i p_i^{a_i} $ ,那么令 \(p = \prod_i p_i^{a_i - 1}, q = \prod_i p_i\) 那么有 \(pq = n\) 。
有一些预备知识可以看看 这篇博客 ,讲了一下如何证明后面的一些结论。
假设你前置的欧拉函数知识都会了,那么我接下来就把 yyb 的博客搬过来啦。
其中有几步比较 \(\text{tricky}\) ,就比如 \(d | \gcd(i, q)\) 变回 \(d | i, d | q\) 然后枚举 \(d | q\) ,算答案。
最后我们利用这个式子直接记忆化处理就行了,对于 \(n = 1\) 的时候用杜教筛处理边界就行了。
复杂度有个比较松的上界 \(\mathcal O(n \sqrt m + m^\frac 2 3)\) ,但好像太松了。。我极限只需要 \(0.6s\) 。(还是用的 std :: map
。。)
代码
#include <bits/stdc++.h>
#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define pb push_back
using namespace std;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("3512.in", "r", stdin);
freopen ("3512.out", "w", stdout);
#endif
}
const int Lim = 1e5, N = Lim + 1e3, Mod = 1e9 + 7;
int prime[N], pcnt, phi[N], sumphi[N], minp[N];
bitset<N> is_prime;
void Linear_Sieve(int maxn) {
is_prime.set(); is_prime[0] = is_prime[1] = false;
phi[1] = sumphi[1] = 1;
For (i, 2, maxn) {
if (is_prime[i]) prime[++ pcnt] = i, phi[i] = i - 1, minp[i] = i;
for (int j = 1, res; (res = i * prime[j]) <= maxn && j <= pcnt; ++ j) {
is_prime[res] = false; minp[res] = prime[j];
if (i % prime[j]) phi[res] = phi[i] * (prime[j] - 1);
else { phi[res] = phi[i] * prime[j]; break; }
}
sumphi[i] = (sumphi[i - 1] + phi[i]) % Mod;
}
}
map<int, int> M, val[N];
#define Out(a, b) if (a) return b;
int Phi(int n) {
Out(n <= Lim, sumphi[n]); Out(M[n], M[n]);
int res = 1ll * n * (n + 1) / 2 % Mod;
for (int i = 2, ni; i <= n; i = ni + 1)
res = (res + Mod - ((ni = n / (n / i)) - i + 1ll) * Phi(n / i)) % Mod;
return M[n] = res;
}
int S(int n, int m) {
Out(!m, 0); Out(n == 1, Phi(m)); Out(m == 1, phi[n]); Out(val[n][m], val[n][m]);
int res = 0, p = 1, q = 1, tmp = n; vector<int> fac;
while (tmp > 1) {
int x = minp[tmp]; q *= x; tmp /= x; fac.pb(x);
while (!(tmp % x)) p *= x, tmp /= x;
}
Rep (i, 1 << int(fac.size())) {
int d = 1;
Rep (j, fac.size()) if (i >> j & 1) d *= fac[j];
res = (res + 1ll * phi[q / d] * S(d, m / d)) % Mod;
}
return val[n][m] = 1ll * res * p % Mod;
}
int main () {
File();
Linear_Sieve(Lim);
int n = read(), m = read(), ans = 0;
For (i, 1, n)
ans = (ans + S(i, m)) % Mod;
printf ("%d\n", ans);
return 0;
}
BZOJ 3560: DZY Loves Math V
题意
给你 \(n\) 个正整数 \(a_1, a_2, \cdots, a_n\) ,求
\(1 \le n \le 10^5,1 \le a_i \le 10^7\)
题解
终于不是那么神的题啊。。。
又发现答案又是一个积性函数。
然后显然对于每个质因子 \(p\) 单独考虑,假设其在 \(a_i\) 中出现 \(b_i\) 次。
那么答案其实就是
考虑当 \(k > 0\) 时有 \(\varphi(p^k) = p^k \times \displaystyle \frac {p - 1} p\) ,对于 \(k = 0\) 时则为 \(\varphi(1) = 1\) 。
那么拆一下就变成
然后对于直接拆回去就行了。
然后分解质因数逐个乘起来就行了,复杂度暴力点是 \(\mathcal O(n \sqrt {a_i})\) 的。
代码
#include <bits/stdc++.h>
#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define mp make_pair
#define fir first
#define sec second
using namespace std;
typedef pair<int, int> PII;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("3560.in", "r", stdin);
freopen ("3560.out", "w", stdout);
#endif
}
const int Lim = 1e7, N = Lim + 1e3, Mod = 1e9 + 7;
inline int fpm(int x, int power) {
int res = 1;
for (; power; power >>= 1, x = 1ll * x * x % Mod)
if (power & 1) res = 1ll * res * x % Mod;
return res;
}
PII fac[N]; int ftot, inv[N];
void ReSolve(int x) {
for (int i = 2; i * i <= x; ++ i) if (!(x % i)) {
int power = 0;
while (!(x % i)) x /= i, ++ power;
fac[++ ftot] = mp(i, power);
}
if (x > 1) fac[++ ftot] = mp(x, 1);
}
int sump[210];
int Solve(int l, int r, int p) {
int res = 1, cur = 0, phip = 1; sump[0] = 1;
For (i, l, r) {
for (; cur < fac[i].sec; ++ cur) {
phip = 1ll * phip * p % Mod;
sump[cur + 1] = (sump[cur] + phip) % Mod;
}
res = 1ll * res * sump[fac[i].sec] % Mod;
}
return 1ll * (res - 1) * (p - 1) % Mod * fpm(p, Mod - 2) % Mod + 1;
}
int main () {
File();
int n = read();
For (i, 1, n) ReSolve(read());
sort(fac + 1, fac + ftot + 1);
int ans = 1;
for (int i = 1, j; i <= ftot; i = j + 1) {
for (j = i; j < ftot && fac[j + 1].fir == fac[i].fir; ++ j);
ans = 1ll * ans * Solve(i, j, fac[i].fir) % Mod;
}
printf ("%d\n", ans);
return 0;
}
BZOJ 3561: DZY Loves Math VI
题意
给定整数 \(n, m\) 求
\(n, m \le 5 \times 10^5\)
题解
傻吊题。
考虑从小到大枚举 \(d\) ,然后每次把 \(\displaystyle 1 \sim \lfloor \frac n d \rfloor\) 的 \(i^{d - 1}\) 乘上 \(i\) 更新到 \(i^d\) 就行了,然后顺便算下前缀和就行了。
复杂度是调和级数 \(\mathcal O(n \log n)\) 的。
BZOJ 3568: DZY Loves Math VII
题意
已知 \(\mu(n)\) ,求第 \(k\) 小的 \(n\) 。\(\text{I.} |\mu(n)| \le 1,k \le 10^8\)
已知 \(\varphi(n)\) ,求第 \(k\) 小的 \(n\) 。\(\text{II.} \varphi(n) \le 10^{10}, k\le 1000\)
已知 \(d(n)\) ,求第 \(k\) 小的 \(n\) 。\(\text{III.} d(N) \le 10^7,K \le 50\)
题解
\(\mu(n)\)
第一问考虑二分 \(n\) ,对于 \(\sum_{i = 1}^{n} \mu(i)\) 杜教筛即可。
然后这只能算出和,不能确定 \(\{-1, 0, 1\}\) 分别有多少,我们再算一下 \(\sum_{i = 1}^{n} |\mu(i)|\) 的大小就行了,就能先确定 \(0\) 的个数,然后列方程确定 \(\{-1, 1\}\) 的个数了。
如何算呢?容斥一下就行了。
大概意思就是考虑每个含有平方因子的 \(a\) ,都不能被算进去,我们要减掉。
那么利用 \(\mu(x)\) 的容斥性质,在一个质因子的时候算 \(-1\) 减掉,两个加上 \(\cdots\)
\(\varphi(n)\)
我们利用 \(\varphi(n)\) 的计算式。
大力分解 \(\varphi(n)\) ,然后直接算所有合法解,小于 \(10^6\) 暴力枚举试除,大于 \(10^6\) 用 Miller_Rabin
判断是否合法就行了,因为 \(> 10^6\) 的质因子不可能超过一个。
看起来很暴力,其实可以通过。。
\(d(n)\)
反质数加强版。。。
利用 \(d(n)\) 的计算式
大力分解一波,\(K = 1\) 时候指数应该是单调不增的,不然一定可以搞到一个更优解,然后大小判断用 \(\ln\) 。
那么我们基于这个策略进行调整,然后剪剪枝,还需要一个高精度。
这题难点应该就在这问,实现起来十分麻烦。而且剪枝看起来十分不靠谱。。。