【11.27】做題日記
阿新 • • 發佈:2020-11-27
杜教篩模板
又寫了一遍,發現之前寫的有錯誤,竟然水過去了,我線篩今天寫錯了兩遍,我以後再寫錯我是傻逼。
#include<bits/stdc++.h> using namespace std; const int N = 3e6 + 10; typedef long long ll; ll prime[N], cnt, mu[N], phi[N], sm[N], sp[N]; bool vis[N]; void init() { mu[1] = phi[1] = 1; for (int i = 2; i < N; i++) { if (!vis[i])prime[++cnt] = i, mu[i] = -1, phi[i] = i - 1; for (int j = 1; j <= cnt and prime[j] * i < N; j++) { vis[prime[j] * i] = 1; mu[prime[j] * i] = -mu[i]; phi[prime[j] * i] = (prime[j] - 1) * phi[i]; if (i % prime[j] == 0) { mu[prime[j] * i] = 0; phi[prime[j] * i] = phi[i] * prime[j]; break; } } } for (int i = 1; i < N; i++) sm[i] = sm[i - 1] + mu[i], sp[i] = sp[i - 1] + phi[i]; } unordered_map<ll, ll>M, P; ll m(ll n) { if (n < N)return sm[n]; if (M.count(n)) return M[n]; ll ans = 1; for (ll l = 2, r; l <= n; l = r + 1) { r = n / (n / l); ans -= (r - l + 1) * m(n / l); } return M[n] = ans; } ll p(ll n) { if (n < N)return sp[n]; if (P.count(n)) return P[n]; ll ans = n * (n + 1) / 2; for (ll l = 2, r; l <= n; l = r + 1) { r = n / (n / l); ans -= (r - l + 1) * p(n / l); } return P[n] = ans; } ll T, n; int main() { init(); scanf("%lld", &T); while (T--) { scanf("%lld", &n); printf("%lld %lld\n", p(n), m(n)); } }
P1390 公約數的和 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)
\[\sum_{i=1}^n\sum_{j = i + 1}^ngcd(i,j) \]求解:
我們求 \(t = \sum_{i=1}^n\sum_{j=1}^ngcd(i,j)=2\sum_{i=1}^n\sum_{j = i + 1}^ngcd(i,j) + \sum_{i=1}^n\)
\[\sum_{i=1}^n\sum_{j=1}^ngcd(i,j)=\sum_{i=1}^n\sum_{j=1}^n\sum_{d|gcd(i,j)}\varphi(d)\\=\sum_{d=1}^n\varphi(d) \lfloor \dfrac ni \rfloor\lfloor \dfrac ni \rfloor \]直接 \(O(n)\) 求就可以了,不需要分塊,因為預處理就需要 \(O(n)\) 的時間差不了多少
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e6 + 10; ll phi[N], prime[N], cnt, vis[N]; ll sum[N]; void init(){ phi[1] = 1; for(int i = 2; i < N;i++){ if (!vis[i])prime[++cnt] = i, phi[i] = i - 1; for(int j = 1; j <= cnt and prime[j] * i < N;j++){ phi[prime[j] * i] = phi[i] * (prime[j] - 1); vis[prime[j] * i] = 1; if(i % prime[j] == 0){ phi[prime[j] * i] = phi[i] * prime[j]; break; } } } for (int i = 1;i < N;i++)sum[i] = sum[i - 1] + phi[i]; } int main(){ init(); int n;scanf("%d", &n); long long ans = 0; for(int i = 1;i <= n;i++){ ans -= i; ans += phi[i] * (n / i) * (n / i); } ans >>= 1; printf("%lld\n", ans); }
還偷來了一個別人的篩素數的東西,但是跑的在 51nod 上跑的好像根 min25 差不了多少
#6235. 區間素數個數 - 題目 - LibreOJ (loj.ac)
P3912 素數個數 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)
min25
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e6 + 10;
int prime[maxn], cnt, vis[maxn];
int g[maxn], tot, w[maxn], idx1[maxn], idx2[maxn];
int sqr, n;
signed main() {
cin >> n;
sqr = sqrt(n + 0.5);
for (int i = 2; i <= sqr; i++) {
if (!vis[i]) {
prime[++cnt] = i;
}
for (int j = 1; j <= cnt && prime[j] * i <= sqr; j++) {
vis[prime[j]*i] = 1;
if (!i % prime[j])
break;
}
}
for (int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
w[++tot] = n / l;
g[tot] = w[tot] - 1;
if (w[tot] <= sqr)
idx1[w[tot]] = tot;
else
idx2[n / w[tot]] = tot;
}
for (int i = 1; i <= cnt; i++) {
for (int j = 1; j <= tot && prime[i] * prime[i] <= w[j]; j++) {
int k = (w[j] / prime[i] <= sqr) ? idx1[w[j] / prime[i]] : idx2[n / (w[j] / prime[i])];
g[j] = g[j] - (g[k] - i + 1);
}
}
cout << g[1] << endl;
}
別人的
暫時沒懂
/*
* @Author: zhl
* @LastEditTime: 2020-11-27 10:24:11
*/
#include<bits/stdc++.h>
#define int long long
using namespace std;
using ll = long long;
int count(ll N) {
int v = sqrt(N);
vector<int> smalls(v + 1), larges(v + 1);
for (int i = 1; i <= v; ++i) smalls[i] = i - 1;
for (int i = 1; i <= v; ++i) larges[i] = N / i - 1;
for (int p = 2; p <= v; ++p) if (smalls[p] > smalls[p - 1]) {
int q = p * p, c = smalls[p - 1], e = min(v, N / q);
for (int i = 1; i <= e; ++i) {
larges[i] -= ((i * p <= v) ? larges[i * p] : smalls[N / (i * p)]) - c;
}
for (int i = v; i >= q; --i) smalls[i] -= smalls[i / p] - c;
}
return larges[1];
}
signed main(){
ll n;
cin >> n;
cout << count(n) << endl;
}
下面的是min25
這個程式碼是真的牛皮
偷過來的跑了第一
#include <cstdio>
#include <cassert>
#include <cmath>
#include <vector>
//Min_25
using namespace std;
using i64 = long long;
int isqrt(i64 n) {
return sqrtl(n);
}
i64 prime_pi(const i64 N) {
if (N <= 1) return 0;
if (N == 2) return 1;
const int v = isqrt(N);
int s = (v + 1) / 2;
vector<int> smalls(s); for (int i = 1; i < s; ++i) smalls[i] = i;
vector<int> roughs(s); for (int i = 0; i < s; ++i) roughs[i] = 2 * i + 1;
vector<i64> larges(s); for (int i = 0; i < s; ++i) larges[i] = (N / (2 * i + 1) - 1) / 2;
vector<bool> skip(v + 1);
const auto divide = [] (i64 n, i64 d) -> int { return double(n) / d; };
const auto half = [] (int n) -> int { return (n - 1) >> 1; };
int pc = 0;
for (int p = 3; p <= v; p += 2) if (!skip[p]) {
int q = p * p;
if (i64(q) * q > N) break;
skip[p] = true;
for (int i = q; i <= v; i += 2 * p) skip[i] = true;
int ns = 0;
for (int k = 0; k < s; ++k) {
int i = roughs[k];
if (skip[i]) continue;
i64 d = i64(i) * p;
larges[ns] = larges[k] - (d <= v ? larges[smalls[d >> 1] - pc] : smalls[half(divide(N, d))]) + pc;
roughs[ns++] = i;
}
s = ns;
for (int i = half(v), j = ((v / p) - 1) | 1; j >= p; j -= 2) {
int c = smalls[j >> 1] - pc;
for (int e = (j * p) >> 1; i >= e; --i) smalls[i] -= c;
}
++pc;
}
larges[0] += i64(s + 2 * (pc - 1)) * (s - 1) / 2;
for (int k = 1; k < s; ++k) larges[0] -= larges[k];
for (int l = 1; l < s; ++l) {
int q = roughs[l];
i64 M = N / q;
int e = smalls[half(M / q)] - pc;
if (e < l + 1) break;
i64 t = 0;
for (int k = l + 1; k <= e; ++k) t += smalls[half(divide(M, roughs[k]))];
larges[0] += t - i64(e - l) * (pc + l - 1);
}
return larges[0] + 1;
}
int main() {
i64 N;
while (~scanf("%lld", &N)) {
printf("%lld\n", prime_pi(N));
}
}