BZOJ 3309: DZY Loves Math I
题意
f(n) 为 n 幂指数的最大值。
a∑i=1b∑j=1f(gcd(i,j))
T≤10000,1≤a,b≤107
题解
ans=a∑i=1b∑j=1f(gcd(i,j))=min(a,b)∑d=1f(d)min(⌊ad⌋,⌊bd⌋)∑x=1μ(x)⌊adx⌋⌊bdx⌋
令 dx=T 那么有
ans=min(a,b)∑T=1⌊aT⌋⌊bT⌋(∑d|Tf(d)μ(Td))
我们线性筛出 f∗μ 就行了,每次整除分块回答,复杂度是 O(n+T√n) 的。
什么不会线性筛?那么埃氏筛卡常吧。
BZOJ 3462: DZY Loves Math II
题意

2≤S≤2∗106,1≤n≤1018,1≤q≤105
题解
好神啊。。
我们考虑把相同的 pi 合并,那么就变成
∑i≤kpici=n(ci≥1)∏i≤kpi=S
求第一个方程 ci 解的方案数。那么对于 S 的唯一分解来说,不存在一个质因子的次数 >1 。
对于 ci≥1 的限制我们可以把 n−∑ipi 来去掉,那么现在我们只需要求一个完全背包的方案数。
对于一个因子 pi 选 ci 个的体积是 pici ,其实就等价于 xS+ypi 这个方案。
什么意思呢?我们把 pici 对于 S 取模后就得到了 ypi ,也就意味着 ypi<S 。
那么我们要求的其实就是
∑i≤k(xS+ypi)=n(ci≥1,ypi<S)
的方案数。
考虑移项,那么就变成
∑i≤kypi=n−∑i≤kxS(ci≥1,y<Spi)
不难发现对于前者的大小不会超过 kS ,可以直接暴力做多重背包就行了。
那么对于后面的方案如何算呢?不难发现其实就是求 ∑i≤kx=n−∑i≤kypiS 的方案数,直接隔板一下就行了。
复杂度其实是 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 求满足方程 xy≡Q(modP) 的整数解 (x,y) 的数量,满足 0≤x,y<P 。
其中 P=∏i≤NPi,Q=∏i≤NQi 。
1≤N≤10,0≤Qi≤1018,1≤Pi≤1018,P≥2
题解
考虑枚举一个 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′ 个(显然有 q′≤q )。
然后贡献其实就是
ans=q′∑i=0pi⋅φ(pq−i)=q′∑i=0pi⋅(p−1)pq−i−1=q′(p−1)pq−1
但是这个会在 q′=q 的时候出现问题,因为 φ(1)=1 那么我们就会把个 pq 算成 (p−1)pq−1 ,加回去即可。
总结
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 求
n∑i=1m∑j=1φ(ij)(mod109+7)
1≤n≤105,1≤m≤109
题解
神题啊!
为啥 n,m 不是同级的?因为是让我们暴力枚举 n 。
那么我们求得就是
S(n,m)=m∑i=1φ(ni)
考虑如何将 φ(ni) 给拆开,因为 φ(x) 只有每个质因子 p 第一次出现的时候才会是 p−1 ,其他时候都直接乘 p 。
我们设 n=∏ipaii ,那么令 p=∏ipai−1i,q=∏ipi 那么有 pq=n 。
有一些预备知识可以看看 这篇博客 ,讲了一下如何证明后面的一些结论。
假设你前置的欧拉函数知识都会了,那么我接下来就把 yyb 的博客搬过来啦。
S(n,m)=pm∑i=1φ(qi)=pm∑i=1φ(q)φ(igcd(i,q))gcd(i,q)=pm∑i=1φ(qgcd(i,q))φ(i)gcd(i,q)=pm∑i=1φ(qgcd(i,q))φ(i)∑d|gcd(i,q)φ(d)=pm∑i=1φ(i)∑d|i,d|qφ(qd)=p∑d|qφ(qd)md∑i=1φ(id)=p∑d|qφ(qd)S(d,⌊md⌋)
其中有几步比较 tricky ,就比如 d|gcd(i,q) 变回 d|i,d|q 然后枚举 d|q ,算答案。
最后我们利用这个式子直接记忆化处理就行了,对于 n=1 的时候用杜教筛处理边界就行了。
复杂度有个比较松的上界 O(n√m+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|a1∑i2|a2⋯∑in|anφ(i1i2⋯in)(mod109+7)
1≤n≤105,1≤ai≤107
题解
终于不是那么神的题啊。。。
又发现答案又是一个积性函数。
然后显然对于每个质因子 p 单独考虑,假设其在 ai 中出现 bi 次。
那么答案其实就是
ans=∏pb1∑i1=0b2∑i2=0…bn∑in=0φ(p∑nj=1ij)
考虑当 k>0 时有 φ(pk)=pk×p−1p ,对于 k=0 时则为 φ(1)=1 。
那么拆一下就变成
ans=∏p(((b1∑i1=0b2∑i2=0…bn∑in=0p∑nj=1ij)−1)×p−1p+1)
然后对于直接拆回去就行了。
ans=∏p((n∏i=1bi∑j=0pj)−1)×p−1p+1)
然后分解质因数逐个乘起来就行了,复杂度暴力点是 O(n√ai) 的。
代码
#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∑i=1m∑j=1lcm(i,j)gcd(i,j)(mod109+7)
n,m≤5×105
题解
傻吊题。
ans=n∑d=1ddmin(⌊nd⌋,⌊md⌋)∑x=1μ(x)x2d(⌊nd⌋∑i=1id)(⌊md⌋∑j=1jd)(mod109+7)
考虑从小到大枚举 d ,然后每次把 1∼⌊nd⌋ 的 id−1 乘上 i 更新到 id 就行了,然后顺便算下前缀和就行了。
复杂度是调和级数 O(nlogn) 的。
BZOJ 3568: DZY Loves Math VII
题意
已知 μ(n) ,求第 k 小的 n 。I.|μ(n)|≤1,k≤108
已知 φ(n) ,求第 k 小的 n 。II.φ(n)≤1010,k≤1000
已知 d(n) ,求第 k 小的 n 。III.d(N)≤107,K≤50
题解
μ(n)
第一问考虑二分 n ,对于 ∑ni=1μ(i) 杜教筛即可。
然后这只能算出和,不能确定 {−1,0,1} 分别有多少,我们再算一下 ∑ni=1|μ(i)| 的大小就行了,就能先确定 0 的个数,然后列方程确定 {−1,1} 的个数了。
如何算呢?容斥一下就行了。
n∑i=1|μ(i)|=n+⌊√n⌋∑i=2μ(i)⌊ni2⌋
大概意思就是考虑每个含有平方因子的 a ,都不能被算进去,我们要减掉。
那么利用 μ(x) 的容斥性质,在一个质因子的时候算 −1 减掉,两个加上 ⋯
φ(n)
我们利用 φ(n) 的计算式。
φ(n)=∏ppkp−1p
大力分解 φ(n) ,然后直接算所有合法解,小于 106 暴力枚举试除,大于 106 用 Miller_Rabin
判断是否合法就行了,因为 >106 的质因子不可能超过一个。
看起来很暴力,其实可以通过。。
d(n)
反质数加强版。。。
利用 d(n) 的计算式
d(n)=∏p(k+1)
大力分解一波,K=1 时候指数应该是单调不增的,不然一定可以搞到一个更优解,然后大小判断用 ln 。
那么我们基于这个策略进行调整,然后剪剪枝,还需要一个高精度。
这题难点应该就在这问,实现起来十分麻烦。而且剪枝看起来十分不靠谱。。。
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 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】