优美函数 题解
题意简述
给你函数
对于一个长度为
给你
题目分析
显然要先观察
lint res = 0; for (int i = 1; i <= 1000000; ++i) { if (i == 1) res += u - a[j]; else if (i <= a[j] && gcd(i, a[j]) == 1) res += u; else if (gcd(i, a[j]) == i) res += -i * a[j]; }
显然要把 if
是不交的,可以分别考虑。发现此时
lint res = u - a[j]; lint cnt = 0, sum = 0; for (int i = 2; i <= a[j]; ++i) { if (gcd(i, a[j]) == 1) ++cnt; if (a[j] % i == 0) sum += i; } res += cnt * u, res -= sum;
我们惊奇地发现,for
里面两个 if
的条件,可以放回去:
lint cnt = 0, sum = 0; for (int i = 1; i <= a[j]; ++i) { if (gcd(i, a[j]) == 1) ++cnt; if (a[j] % i == 0) sum += i; } lint res = cnt * u - sum;
这时候,
可是我不会
考场上忘了,只好现推一遍。其实以前根本不知道啥是 我们只用考虑
-
。 。
经典积性函数曾说:如果你给我 ,我能告诉你, 。所以 。 。
我们记 。考虑欧拉函数计算式 ,就有 。而 ,所以:
-
。
考虑 ,那么 ,由于 之间互不影响,考虑乘法原理, ,这样一定能凑出所有 的因数。 。
。这里没有一步到位的原因是,我之前不知道 ,也就没想它是不是积性函数了。 。
同样设 中有 ,那么 ,这很难受,考虑变换一下 。接下来,我们只需要对一个 维护 表示 ,其中 是 的最小质因数, 是幂次。这个很好维护,当 , ;否则, 。如此,
对于一次询问,求的式子可以简化成:
对于一个
一次函数最值,且
时间复杂度:
代码
#include <cstdio> #include <iostream> #include <vector> using namespace std; const int N = 500010, V = 1000010; using lint = long long; int n, q, val[N]; int phi[V], prime[V], pcnt, cj[V]; bool nprime[V]; lint subsum[V], K[N], B[N]; vector<pair<int, int>> qry[N]; lint mx[N]; int mxR[N]; int stack[N], top; inline bool cmptail(int a, int b, int c) { return (__int128_t)(B[c] - B[a]) * (K[a] - K[b]) > (__int128_t)(B[b] - B[a]) * (K[a] - K[c]); } inline void insert(int idx) { while (top - 1 >= 1 && cmptail(stack[top - 1], stack[top], idx)) --top; stack[++top] = idx; } inline int query(int x) { int l = 1, r = top - 1, res = stack[top]; while (l <= r) { int mid = (l + r) >> 1; if (1ll * x * (K[stack[mid]] - K[stack[mid + 1]]) >= B[stack[mid + 1]] - B[stack[mid]]) res = stack[mid], r = mid - 1; else l = mid + 1; } return res; } signed main() { #ifndef XuYueming freopen("function.in", "r", stdin); freopen("function.out", "w", stdout); #endif scanf("%d%d", &n, &q); for (int i = 1; i <= n; ++i) scanf("%d", &val[i]); phi[1] = 1, subsum[1] = 1; for (int i = 2; i <= 1000000; ++i) { if (!nprime[i]) prime[++pcnt] = i, phi[i] = i - 1, subsum[i] = 1 + i, cj[i] = i; for (int j = 1; j <= pcnt && i * prime[j] <= 1000000; ++j) { nprime[i * prime[j]] = true; if (i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; subsum[i * prime[j]] = subsum[i] + subsum[i / cj[i]] * cj[i] * prime[j]; cj[i * prime[j]] = cj[i] * prime[j]; break; } phi[i * prime[j]] = phi[i] * phi[prime[j]]; subsum[i * prime[j]] = subsum[i] * subsum[prime[j]]; cj[i * prime[j]] = prime[j]; } } for (int i = 1; i <= n; ++i) { K[i] = phi[val[i]]; B[i] = -1ll * subsum[val[i]] * val[i]; K[i] += K[i - 1]; B[i] += B[i - 1]; } for (int i = 1, u, l; i <= q; ++i) { scanf("%d%d", &u, &l); qry[l].emplace_back(u, i); } for (int i = n; i >= 1; --i) { insert(i); for (auto [x, idx]: qry[i]) { int ps = query(x); mx[idx] = (1ll * x * K[ps] + B[ps]) - (1ll * x * K[i - 1] + B[i - 1]); mxR[idx] = ps; } } for (int i = 1; i <= q; ++i) printf("%lld %d\n", mx[i], mxR[i]); return 0; }
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18443164。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】