DZY Loves Math 系列详细题解

BZOJ 3309: DZY Loves Math I

题意

f(n)n 幂指数的最大值。

i=1aj=1bf(gcd(i,j))

T10000,1a,b107

题解

ans=i=1aj=1bf(gcd(i,j))=d=1min(a,b)f(d)x=1min(ad,bd)μ(x)adxbdx

dx=T 那么有

ans=T=1min(a,b)aTbT(d|Tf(d)μ(Td))

我们线性筛出 fμ 就行了,每次整除分块回答,复杂度是 O(n+Tn) 的。

什么不会线性筛?那么埃氏筛卡常吧。

BZOJ 3462: DZY Loves Math II

题意

img

2S2106,1n1018,1q105

题解

好神啊。。

我们考虑把相同的 pi 合并,那么就变成

ikpici=n(ci1)ikpi=S

求第一个方程 ci 解的方案数。那么对于 S 的唯一分解来说,不存在一个质因子的次数 >1

对于 ci1 的限制我们可以把 nipi 来去掉,那么现在我们只需要求一个完全背包的方案数。

对于一个因子 pici 个的体积是 pici ,其实就等价于 xS+ypi 这个方案。

什么意思呢?我们把 pici 对于 S 取模后就得到了 ypi ,也就意味着 ypi<S

那么我们要求的其实就是

ik(xS+ypi)=n(ci1,ypi<S)

的方案数。

考虑移项,那么就变成

ikypi=nikxS(ci1,y<Spi)

不难发现对于前者的大小不会超过 kS ,可以直接暴力做多重背包就行了。

那么对于后面的方案如何算呢?不难发现其实就是求 ikx=nikypiS 的方案数,直接隔板一下就行了。

复杂度其实是 O(k2S+qk2) 的,跑的挺快。

总结

对于一类计数问题,可以考虑如何把原来模型等价替代,常常能优化复杂度,或减少难度。

代码

注意多重背包那里可以减掉不合法的优化一下复杂度。

#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 求满足方程 xyQ(modP) 的整数解 (x,y) 的数量,满足 0x,y<P

其中 P=iNPi,Q=iNQi

1N10,0Qi1018,1Pi1018,P2

题解

考虑枚举一个 x ,那么我们就是求对于 yx+kP=Q 的整数解个数。

利用在扩欧学习的知识,只有在 gcd(x,P)|Q 的时候才会有解,且在这个模意义下有 gcd(x,P) 个解。

那么答案其实就是

x<Pgcd(x,P)[gcd(x,P)|Q]

考虑枚举 gcd(P,Q)=T 那么答案其实就是

f(T)=d|Tdφ(Pd)

如果你足够厉害,就能发现 f(T) 其实是个积性函数。

其实是因为积性函数和完全积性函数的狄利克雷卷积是个积性函数。。。(把 P 那里提出一个因子变成 T 就行了)

那么我们就能对于每个质因子 p 单独考虑啦,设它在 P 中有 q 个,T 中有 q 个(显然有 qq )。

然后贡献其实就是

ans=i=0qpiφ(pqi)=i=0qpi(p1)pqi1=q(p1)pq1

但是这个会在 q=q 的时候出现问题,因为 φ(1)=1 那么我们就会把个 pq 算成 (p1)pq1 ,加回去即可。

总结

yx+kP=Q 有在 gcd(x,P)|Q 的时候才会有解,且在这个模 P 意义下有 gcd(x,P) 个解。

注意观察积性函数的性质,用质数去算答案大大降低复杂度。

代码

要个 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

i=1nj=1mφ(ij)(mod109+7)

1n105,1m109

题解

神题啊!

为啥 n,m 不是同级的?因为是让我们暴力枚举 n

那么我们求得就是

S(n,m)=i=1mφ(ni)

考虑如何将 φ(ni) 给拆开,因为 φ(x) 只有每个质因子 p 第一次出现的时候才会是 p1 ,其他时候都直接乘 p

我们设 n=ipiai ,那么令 p=ipiai1,q=ipi 那么有 pq=n

有一些预备知识可以看看 这篇博客 ,讲了一下如何证明后面的一些结论。

假设你前置的欧拉函数知识都会了,那么我接下来就把 yyb 的博客搬过来啦。

S(n,m)=pi=1mφ(qi)=pi=1mφ(q)φ(igcd(i,q))gcd(i,q)=pi=1mφ(qgcd(i,q))φ(i)gcd(i,q)=pi=1mφ(qgcd(i,q))φ(i)d|gcd(i,q)φ(d)=pi=1mφ(i)d|i,d|qφ(qd)=pd|qφ(qd)i=1mdφ(id)=pd|qφ(qd)S(d,md)

其中有几步比较 tricky ,就比如 d|gcd(i,q) 变回 d|i,d|q 然后枚举 d|q ,算答案。

最后我们利用这个式子直接记忆化处理就行了,对于 n=1 的时候用杜教筛处理边界就行了。

复杂度有个比较松的上界 O(nm+m23) ,但好像太松了。。我极限只需要 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 个正整数 a1,a2,,an ,求

i1|a1i2|a2in|anφ(i1i2in)(mod109+7)

1n1051ai107

题解

终于不是那么神的题啊。。。

又发现答案又是一个积性函数。

然后显然对于每个质因子 p 单独考虑,假设其在 ai 中出现 bi 次。

那么答案其实就是

ans=pi1=0b1i2=0b2in=0bnφ(pj=1nij)

考虑当 k>0 时有 φ(pk)=pk×p1p ,对于 k=0 时则为 φ(1)=1

那么拆一下就变成

ans=p(((i1=0b1i2=0b2in=0bnpj=1nij)1)×p1p+1)

然后对于直接拆回去就行了。

ans=p((i=1nj=0bipj)1)×p1p+1)

然后分解质因数逐个乘起来就行了,复杂度暴力点是 O(nai) 的。

代码

#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

i=1nj=1mlcm(i,j)gcd(i,j)(mod109+7)

n,m5×105

题解

傻吊题。

ans=d=1nddx=1min(nd,md)μ(x)x2d(i=1ndid)(j=1mdjd)(mod109+7)

考虑从小到大枚举 d ,然后每次把 1ndid1 乘上 i 更新到 id 就行了,然后顺便算下前缀和就行了。

复杂度是调和级数 O(nlogn) 的。

BZOJ 3568: DZY Loves Math VII

题意

已知 μ(n) ,求第 k 小的 nI.|μ(n)|1,k108

已知 φ(n) ,求第 k 小的 nII.φ(n)1010,k1000

已知 d(n) ,求第 k 小的 nIII.d(N)107,K50

题解

μ(n)

第一问考虑二分 n ,对于 i=1nμ(i) 杜教筛即可。

然后这只能算出和,不能确定 {1,0,1} 分别有多少,我们再算一下 i=1n|μ(i)| 的大小就行了,就能先确定 0 的个数,然后列方程确定 {1,1} 的个数了。

如何算呢?容斥一下就行了。

i=1n|μ(i)|=n+i=2nμ(i)ni2

大概意思就是考虑每个含有平方因子的 a ,都不能被算进去,我们要减掉。

那么利用 μ(x) 的容斥性质,在一个质因子的时候算 1 减掉,两个加上

φ(n)

我们利用 φ(n) 的计算式。

φ(n)=ppkp1p

大力分解 φ(n) ,然后直接算所有合法解,小于 106 暴力枚举试除,大于 106Miller_Rabin 判断是否合法就行了,因为 >106 的质因子不可能超过一个。

看起来很暴力,其实可以通过。。

d(n)

反质数加强版。。。

利用 d(n) 的计算式

d(n)=p(k+1)

大力分解一波,K=1 时候指数应该是单调不增的,不然一定可以搞到一个更优解,然后大小判断用 ln

那么我们基于这个策略进行调整,然后剪剪枝,还需要一个高精度。

这题难点应该就在这问,实现起来十分麻烦。而且剪枝看起来十分不靠谱。。。


__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/10540624.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(839)  评论(4编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示