voidGospersHack(int n, int k){ int cur = (1 << k) - 1; int limit = (1 << n); std::vector<int> r; while (cur < limit) { // do something int lb = cur & -cur; int r = cur + lb; cur = (((r ^ cur) >> 2) / lb) | r; } }
intmain(){ //freopen("in","r",stdin); std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int l = 1, r = 1e6; while (l <= r) { int m = (l + r) / 2; std::cout << m << std::endl; std::string s; std::cin >> s; if (s[0] == '<') r = m - 1; else l = m + 1; } std::cout << "! " << r << std::endl; return0; }
LL floorSum(int n, int m, int a, int b){ LL r = 0; if (a >= m) { r += LL(a / m) * (n - 1) * n / 2; a %= m; } if (b >= m) { r += LL(b / m) * n; b %= m; } int yMax = (LL(a) * n + b) / m; if (yMax == 0) return r; LL xMax = LL(yMax) * m - b; r += (n - (xMax + a - 1) / a) * yMax; r += floorSum(yMax, a, m, (a - (xMax % a)) % a); return r; }
当然我们可以考虑用长方形整点数减去上部分的整点数(要往上平移一个单位)这样搞更快。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
LL floorSum(int n, int m, int a, int b){ LL r = 0; if (a >= m) { r += LL(a / m) * (n - 1) * n / 2; a %= m; } if (b >= m) { r += LL(b / m) * n; b %= m; } int yMax = (LL(a) * n + b) / m; if (yMax == 0) return r; r += LL(n - 1) * yMax; r -= floorSum(yMax, a, m, m - b - 1); return r; }
// 简洁写法,不推荐,推荐使用内建 __gcd LL gcd(LL a, LL b){ return b ? gcd(b, a % b) : a; } // lambda 表达式写法,开头不能是auto因为递归 std::function<int(int, int)> gcd = [&](int a, int b)->int{ return b == 0 ? a : gcd(b, a % b); }; // 快速版本 https://cp-algorithms.com/algebra/euclid-algorithm.html LL gcd(LL a, LL b){ if (!a || !b) return a | b; unsigned shift = __builtin_ctzll(a | b); a >>= __builtin_ctzll(a); do { b >>= __builtin_ctzll(b); if (a > b) std::swap(a, b); b -= a; } while (b); return a << shift; } // 普通版拓展GCD LL exGcd(LL a, LL b, LL& x, LL& y){ // ax + by = gcd(a,b) if (b == 0){ x = 1; y = 0; return a; } LL d = exgcd(b, a % b, y, x); y -= a / b * x; return d; } // C++17版拓展GCD,优雅了不少! std::tuple<LL, LL, LL> exGcd(LL a, LL b) { // ax + by = gcd(a,b) if (b == 0) return {a, 1, 0}; auto [d, y, x] = exGcd(b, a % b); return {d, x, y - a / b * x}; }
Sum of least common multiple $s_n = \sum_{i=1} ^n lcm(i,n)$
所以,我们可以在 $O(n \log n)$ 处理好 $s_n$ 的前 $n$ 项。
Double sum of least common multiple $ds_n = \sum_{1 \leq i \leq j \leq n} lcm(i,j)$
LL inv(LL a, LL p){ // 0 < a < p and gcd(a,p) = 1 return a == 1 ? 1 : (p - p / a) * inv(p % a, p) % p; }
上述代码主要用于线性时间预处理所有$p$以内的逆元,对于较小的常数$a$, 可以直接试除 b p mod a == 1 用下面快速幂也可以求逆,inv 的步数平均下面显著的比快速幂小,但是由于用到了递归,因此最终它们的平均效率是一致的。 可以通过预处理小部分值达到快速的效果。
快速模乘法
1 2 3 4 5 6 7 8 9
// 0 <= x < p < INT_MAX LL powMod(LL x, LL n, LL p){ LL r = 1; while (n) { if (n&1) r = r * x % p; n >>= 1; x = x * x % p; } return r; }
可以用 for 循环写的更短一点,但没必要,若 p < INT64_MAX 就需要使用 __int128 了。现代计算机都是 64 位的,因此处理 int 跟 long long 基本没有时间差异,直接用 LL 避免了强制类型转化,因此效率反而更高。但是 __int128 就不同了。
1 2 3 4 5 6 7 8 9
// 0 <= x < p < INT64_MAX LL powMod(LL x, LL n, LL p){ LL r = 1; while (n) { if (n&1) r = __int128(r) * x % p; n >>= 1; x = __int128(x) * x % p; } return r; }
constint M = 1e9 + 7; constint N = 3e5 + 2; LL fac[N], ifac[N]; LL inv(LL a){ return a == 1 ? 1 : (M - M / a) * inv(M % a) % M; } voidinit(){ fac[0] = 1; for (int i = 1; i < N; ++i) fac[i] = fac[i - 1] * i % M; ifac[N - 1] = inv(fac[N - 1]); for (int i = N - 1; i; --i) ifac[i - 1] = ifac[i] * i % M; } LL binom(int n, int k){ //if (n < k || n < 0) return 0; return fac[n] * ifac[k] % M * ifac[n - k] % M; } LL lucas(LL n, LL m, LL p){ // C(n,m)%p, 仅在p较少时发挥作用 LL r = 1; while (n && m) { LL np = n % p, mp = m % p; if (np < mp) return0; r = binom(np, mp); n /= p, m /= p; } return r; }
boolisPrime(int n){ if (n == 2) returntrue; if (n == 1 || n % 2 == 0) returnfalse; for (int i = 3; i * i <= n; i += 2){ if (n % i == 0) returnfalse; } returntrue; }
$O(n \log n)$ 素数筛
1 2 3 4 5 6 7 8 9 10 11 12 13
constint N = 1e7+2; bool isP[N];
voidinitPrime(){ if(isP[2]) return; isP[2] = true; for (int i = 3; i < N; i += 2) isP[i] = true; for (int i = 3; i < N; i += 2) if (isP[i]) { for (int j = 3 * i; j < N; j += i * 2) { isP[j] = false; } } }
欧拉线性素数筛(正式使用版)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
constint N = 1e9 + 9; bool isp[N]; std::vector<int> p; intinitPrimeP(){ p.emplace_back(2); isp[2] = true; for (int i = 3; i < N; i += 2) isp[i] = true; int sq = int(std::sqrt(N + 0.1))|1; for (int i = 3; i <= sq; i += 2) if (isp[i]) { p.emplace_back(i); for (int j = i * i; j < N; j += i << 1) { isp[j] = false; } } for (int i = sq + 2; i < N; i += 2) if (isp[i]) p.emplace_back(i); return p.size(); }
欧拉线性素数筛(弃用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
constint N = 1e9 + 9; bool isp[N]; std::vector<int> p;
voidinitPrimeP(){ p.emplace_back(2); isp[2] = true; for (int i = 3; i < N; i += 2) isp[i] = true; for (int i = 3; i < N; i += 2) { if (isp[i]) p.emplace_back(i); for (int j = 1, t = (N - 1) / i + 1; j != p.size() && p[j] < t; ++j) { // 用除号是防止溢出 isp[i * p[j]] = false; // 不要下面的一步的话,复杂度 O(nloglogn), 但是不用除法,常数小 if (i % p[j] == 0) break; } } }
namespace PollardRho { std::mt19937 rnd(std::chrono::steady_clock::now().time_since_epoch().count()); LL powMod(LL x, LL n, LL p){ LL r = 1; while (n) { if (n & 1) r = __int128(r) * x % p; n >>= 1; x = __int128(x) * x % p; } return r; } // 1 < a < n,若 n 是素数,那么 a^(n - 1) = 1 mod n // m - 1 = m * 2 ^ t,返回 false 表示判断失败 boolwitness(LL a, LL n, LL m, int t){ LL x = powMod(a, m, n); if (x == 1 || x == n - 1) returnfalse; while (t--) { x = __int128(x) * x % n; if (x == n - 1) returnfalse; } returntrue; } constint TIMES = 52; boolrabin(LL n){ if (n < 2) returnfalse; if (n == 2) returntrue; if (n % 2 == 0) returnfalse; LL m = n - 1; int t = __builtin_ctzll(m); m >>= t; for (int cnt = 0; cnt < TIMES; ++cnt) { LL a = rnd() % (n - 1) + 1; if (witness(a, n, m, t)) returnfalse; } returntrue; } LL pollardrho(LL n){ LL x = 0, y = 0, z = 1, i = 1, k = 2, c = rnd() % (n - 1) + 1; while (true) { x = (__int128(x) * x + c) % n; z = __int128(y - x + n) * z % n; // 累计 gcd 一次计算!太猛了啊 茶茶白 if (++i == k) { LL d = std::__gcd(z, n); if (d > 1) return d; y = x; if (k > n) return n; k <<= 1; } } } LL spf(LL n){ if (rabin(n) || n == 1) return n; LL d = n; while (d == n) d = pollardrho(n); returnstd::min(spf(d), spf(n / d)); } LL gpf(LL n, LL mxf = 1){ if (rabin(n)) return n; if (n <= mxf) return1; LL d = n; while (d == n) d = pollardrho(n); LL res = gpf(d, mxf); returnstd::max(res, gpf(n / d, std::max(res, mxf))); } };
$O(n)$ 最小素因子预处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
constint N = 2e8; // 再大内存吃不消了 int sp[N], p[N]; intspf(){ int cnt = 1; p[1] = 2; for (int i = 2; i < N; i += 2) sp[i] = 2; for (int i = 1; i < N; i += 2) sp[i] = i; for (int i = 3; i < N; i += 2) { if (sp[i] == i) p[++cnt] = i; for (int j = 1; j <= cnt && p[j] <= sp[i] && i * p[j] < N; ++j) { //防止乘法溢出 sp[i * p[j]] = p[j]; // 注意到sp只被赋值一次 } } return cnt; }
Möbius function
另类递推公式: $ \mu(i) = - \sum_{d \mid i, d < i} \mu(d) $。
// 乞丐版 intgetMu(int n){ if (n % 4 == 0) return0; int r = 1; if (n % 2 == 0 ){ n /= 2; r = -r; } for (int i = 3; i * i <= n; i += 2){ if (n % i == 0) { n /= i; if(n % i == 0) return0; r = -r; } } return n > 1 ? -r : r; } // O(n log n) 预处理版本 constint N = 1e6 +2; int mu[N]; voidinitmu(){ mu[1] = 1; for (int i = 1; i < N; ++i) { for (int j = i * 2; j < N; j += i) { mu[j] -= mu[i]; } } }
// O(n) 预处理版 constint N = 1e6 + 2; bool isp[N]; int p[N], mu[N]; intinitmu(){ if(mu[1] == 1) return; int cnt = 1; p[1] = 2; mu[1] = 1; isp[2] = true; for (int i = 3; i < N; i += 2) isp[i] = true; for (int i = 3; i < N; i += 2) { if (isp[i]) mu[i] = -1, p[++cnt] = i; for (int j = 1, t = (N - 1) / i + 1; j <= cnt && p[j] < t; ++j) { isp[i * p[j]] = false; if (i % p[j] == 0) break; mu[i * p[j]] = -mu[i]; } } for (int i = 2; i < N; i += 4) mu[i] = -mu[i >> 1]; return cnt; }
constint N = 1e7+2; int phi[N]; voidinitPhi(){ if (phi[2] == 1) return; for (int i = 1; i < N; i += 2) phi[i] = i; for (int i = 2; i < N; i += 2) phi[i] = i >> 1; for (int i = 3; i < N; i += 2) { if (phi[i] != i) continue; for (int j = i; j < N; j += i) phi[j] = phi[j] / i * (i - 1); } } intgetPhi(int n){ int r = (n % 2 == 0 ? n/2 : n); while (n % 2 == 0) n/=2; for (int i = 3; i * i <= n; i += 2) { if (n % i == 0) { r = r / i *(i-1); while (n % i == 0) n/=i; } } if (n > 1) r = r / n * (n - 1); return r; } LL sumPhi[N]; voidinitSumphi(){ if (phi[2] != 1) initPhi(); for (int i = 1; i < N; ++i) sumPhi[i] = sumPhi[i - 1] + phi[i]; } std::map<int, LL> mp; LL getSumphi(int n){ if (n < N) return (LL) sumPhi[n]; auto it = mp.find(n); if (it != mp.end()) return it->second; LL r = LL(n + 1) * n / 2; for (int i = 2, j; i <= n; i = j + 1) { j = n / (n / i); r -= (j - i + 1) * getSumphi(n / i); } mp.insert(std::make_pair(n, r)); return r; }
LL baby_step_giant_step(LL a, LL b, LL p){ // a^x = b mod p a %= p, b %= p; if (a == 0) return b % p ? -1 : 1; if (b == 1) return0; LL cnt = 0, t = 1; for (LL g = std::__gcd(a, p); g != 1; g = std::__gcd(a, p)) { if (b % g) return-1; p /= g, b /= g, t = t * (a / g) % p; ++cnt; if (b == t) return cnt; } std::map<LL, LL> mp; LL m = LL(std::sqrt(p + 0.1) + 1); LL base = b; for (LL i = 0; i != m; ++i) { mp[base] = i; base = base * a % p; } base = powMod(a, m, p); for (LL i = 1; i <= m + 1; ++i) { t = t * base % p; if (mp.count(t)) return i * m - mp[t] + cnt; } return-1; }
LL modsqrt(LL a, LL p){ // find x s.t x*x=a mod p; a = (p + a % p) % p; if (a == 0) return0; if (p == 2) return (a & 1) ? 1 : 0; LL q = (p - 1) >> 1; if (powMod(a, q, p) != 1) return-1; if (q & 1) return powMod(a, (q + 1) >> 1, p); LL b, cnt = 1; while (powMod(b = rand() % p, q, p) == 1); //find a non quadratic residue while (!(q & 1)) ++cnt, q >>= 1; b = powMod(b, q, p); LL x = powMod(a, (q + 1) >> 1, p); for (LL s = 1, t = powMod(a, q, p); t != 1; s = 1) { //keep x*x=t*a;t^{2^{cnt-1}}=1 for (LL tt = t * t % p; s < cnt && tt != 1; ++s) tt = tt * tt % p; LL d = powMod(b, 1 << (cnt - s - 1), p); x = (x * d) % p; b = d * d % p; t = t * b % p; cnt = s; } return x; } LL mod_sqrt(LL a, LL p, int k = 1){ //find smallest x>=0 s.t x*x=a mod p^k if (a == 0) return0; int ka = 0; while (a % p == 0) a /= p, ++ka, --k; if (k <= 0) return0; if (ka & 1) return-1; autopow = [](LL x, int n) { LL r=1; for (; n; n >>= 1, x *= x) if (n&1) r *= x; return r; }; LL n = pow(p, k), x; if (p == 2) { if (k == 1 || k == 2) x = a == 1 ? 1 : -1; elseif (a % 8 != 1) x = -1; else { x = 1; for (int i = 4; i <= k; ++i) { if ((x * x) % (1 << i) == a % (1 << i)) continue; x += 1 << (i - 2); } } } else x = mod_sqrt_p(a, n, p, k); return x == -1 ? -1 : pow(p, ka >> 1) * (x < n - x ? x : n - x); }
voidadd(int &x, int y){ (x += y) >= P && (x -= P); } voidsub(int &x, int y){ (x -= y) < 0 && (x += P); } structFWT { intextend(int n){ int N = 1; for (; N < n; N <<= 1); return N; } voidFWTor(std::vector<int> &a, bool rev){ int n = a.size(); for (int l = 2, m = 1; l <= n; l <<= 1, m <<= 1) { for (int j = 0; j < n; j += l) for (int i = 0; i < m; i++) { if (!rev) add(a[i + j + m], a[i + j]); else sub(a[i + j + m], a[i + j]); } } } voidFWTand(std::vector<int> &a, bool rev){ int n = a.size(); for (int l = 2, m = 1; l <= n; l <<= 1, m <<= 1) { for (int j = 0; j < n; j += l) for (int i = 0; i < m; i++) { if (!rev) add(a[i + j], a[i + j + m]); else sub(a[i + j], a[i + j + m]); } } } voidFWTxor(std::vector<int> &a, bool rev){ int n = a.size(), inv2 = (P + 1) >> 1; for (int l = 2, m = 1; l <= n; l <<= 1, m <<= 1) { for (int j = 0; j < n; j += l) for (int i = 0; i < m; i++) { int x = a[i + j], y = a[i + j + m]; if (!rev) { a[i + j] = (x + y) % P; a[i + j + m] = (x - y + P) % P; } else { a[i + j] = 1LL * (x + y) * inv2 % P; a[i + j + m] = 1LL * (x - y + P) * inv2 % P; } } } } std::vector<int> Or(std::vector<int> a1, std::vector<int> a2){ int n = std::max(a1.size(), a2.size()), N = extend(n); a1.resize(N), FWTor(a1, false); a2.resize(N), FWTor(a2, false); std::vector<int> A(N); for (int i = 0; i < N; i++) A[i] = 1LL * a1[i] * a2[i] % P; FWTor(A, true); return A; } std::vector<int> And(std::vector<int> a1, std::vector<int> a2){ int n = std::max(a1.size(), a2.size()), N = extend(n); a1.resize(N), FWTand(a1, false); a2.resize(N), FWTand(a2, false); std::vector<int> A(N); for (int i = 0; i < N; i++) A[i] = 1LL * a1[i] * a2[i] % P; FWTand(A, true); return A; } std::vector<int> Xor(std::vector<int> a1, std::vector<int> a2){ int n = std::max(a1.size(), a2.size()), N = extend(n); a1.resize(N), FWTxor(a1, false); a2.resize(N), FWTxor(a2, false); std::vector<int> A(N); for (int i = 0; i < N; i++) A[i] = 1LL * a1[i] * a2[i] % P; FWTxor(A, true); return A; } } fwt;
intmain(){ int n; scanf("%d", &n); std::vector<int> a1(n), a2(n); for (int i = 0; i < n; i++) scanf("%d", &a1[i]); for (int i = 0; i < n; i++) scanf("%d", &a2[i]); std::vector<int> A; A = fwt.Or(a1, a2); for (int i = 0; i < n; i++) { printf("%d%c", A[i], " \n"[i == n - 1]); } A = fwt.And(a1, a2); for (int i = 0; i < n; i++) { printf("%d%c", A[i], " \n"[i == n - 1]); } A = fwt.Xor(a1, a2); for (int i = 0; i < n; i++) { printf("%d%c", A[i], " \n"[i == n - 1]); } return0; }
#include<bits/stdc++.h> #define watch(x) std::cout << (#x) << " is " << (x) << std::endl using LL = longlong; using pii = std::pair<int, int>; using pll = std::pair<LL, LL>;
using VD = std::vector<double>; constdouble eps = 1e-10; constdouble inf = 1e10; // make sure that A = (I, A') and b >= 0, compute max cx VD simplexCore(VD c, std::vector<VD> A, VD b){ int n = A.size(), m = c.size(); std::vector<int> p(m); std::iota(p.begin(), p.end(), 0); for (int i = 0; i < n; ++i) A[i].emplace_back(b[i]); c.emplace_back(0); A.emplace_back(c); for (int j = n; j <= m; ++j) { for (int i = 0; i < n; ++i) { A[n][j] -= A[n][i] * A[i][j]; } } auto check = [&]() -> bool { for (int j = n; j < m; ++j) if (A[n][j] > eps) { bool flag = false; for (int i = 0; i < n; ++i) if (A[i][j] > eps) { flag = true; break; } if (!flag) returnfalse; } returntrue; }; while (1) { int ch = std::max_element(A[n].begin() + n, A[n].begin() + m) - A[n].begin(), hc; if (A[n][ch] < eps) break; assert(check()); // otherwise unbounded, no max solution double theta = DBL_MAX; for (int i = 0; i < n; ++i) if (A[i][ch] > eps && A[i].back() / A[i][ch] < theta) { theta = A[i].back() / A[i][ch]; hc = i; } std::swap(p[ch], p[hc]); double tmp = 1 / A[hc][ch]; for (int j = n; j <= m; ++j) A[hc][j] *= tmp; for (int i = 0; i <= n; ++i) if (i != hc) { for (int j = n; j <= m; ++j) if (j != ch) { A[i][j] -= A[i][ch] * A[hc][j]; } } for (int i = 0; i <= n; ++i) A[i][ch] *= -tmp; A[hc][ch] = tmp; } VD x(m); for (int i = 0; i < n; ++i) x[p[i]] = A[i].back(); // watch(-A.back().back()); // max_val return x; // point Corresponds to max_val } // compute max cx, with Aqx = bq and Alq x <= blq, end of 0 can be ommit in A and Aq VD simplex(VD c, std::vector<VD> Aq, VD bq, std::vector<VD> Alq, VD blq){ assert(Aq.size() == bq.size()); assert(Alq.size() == blq.size()); int n = Aq.size() + Alq.size(); int m = c.size(); for (int i = 0; i < bq.size(); ++i) if (bq[i] < -eps) { for (auto &x : Aq[i]) x = -x; bq[i] = -bq[i]; } for (int i = 0; i < blq.size(); ++i) if (blq[i] < -eps) { for (auto &x : Alq[i]) x = -x; ++m; } std::vector<VD> A(n, VD(n + m)); VD f(n + m), b(n); int now = n + c.size(); for (int i = 0; i < n; ++i) A[i][i] = 1; for (int i = 0; i < Aq.size(); ++i) { for (int j = 0; j < Aq[i].size(); ++j) A[i][n + j] = Aq[i][j]; b[i] = bq[i]; f[i] = -inf; } for (int i = 0; i < Alq.size(); ++i) { for (int j = 0; j < Alq[i].size(); ++j) A[i + Aq.size()][n + j] = Alq[i][j]; if (blq[i] < -eps) { A[i + Aq.size()][now++] = -1; f[i + Aq.size()] = -inf; } b[i + Aq.size()] = fabs(blq[i]); } for (int i = 0; i < c.size(); ++i) f[n + i] = c[i]; auto x = simplexCore(f, A, b); return VD(x.begin() + n, x.begin() + n + c.size()); }
// 初始情形: p[i] = i intfind(int x){ if (p[x] != x) p[x] = find(p[x]); return p[x]; } voidmerge(int i, int j){ // p[find(j)] = p[find(i)]; // In general we should write below, and merge small to big // int fi = find(i), fj = find(j); // if (fi != fj) p[fi] = fj; }
离散化
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 返回的是离散化之后的数组值对应的原始值 template<typename T> std::vector<T> discrete(std::vector<T>& a){ auto b = a; std::sort(b.begin(), b.end()); b.erase(std::unique(b.begin(), b.end()), b.end()); std::vector<T> r(b.size()); for (auto & x : a) { int id = std::lower_bound(b.begin(), b.end(), x) - b.begin(); r[id] = x; x = id; } return r; }
structSegmentTree { int n; std::vector<int> mn, tag; std::vector<LL> sm; #define lson l, m, 2 * p #define rson m + 1, r, 2 * p + 1 voidresize(){ mn.resize(4 * n); tag.resize(4 * n); sm.resize(4 * n); } SegmentTree(int _n) : n(_n) { resize(); } SegmentTree(conststd::vector<int> &a) { n = a.size(); resize(); std::function<void(int, int, int)> build = [&](int l, int r, int p) { if (l == r) { mn[p] = sm[p] = a[l - 1]; return; } int m = (l + r) / 2; build(lson); build(rson); pull(p); }; build(1, n, 1); } voidpull(int p){ mn[p] = std::min(mn[2 * p], mn[2 * p + 1]); sm[p] = sm[2 * p] + sm[2 * p + 1]; } voidpush(int l, int r, int p){ if (tag[p]) { int m = (l + r) / 2; set(lson, tag[p]); set(rson, tag[p]); tag[p] = 0; } } voidset(int l, int r, int p, int v){ tag[p] = mn[p] = v; sm[p] = LL(r - l + 1) * v; } voidrangeSet(int L, int R, int v, int l, int r, int p){ if (L <= l && R >= r) { set(l, r, p, v); return; } int m = (l + r) / 2; push(l, r, p); if (L <= m) rangeSet(L, R, v, lson); if (R > m) rangeSet(L, R, v, rson); pull(p); } // 以下内容根据需要修改 intquery(int x, int& y, int l, int r, int p){ if (mn[p] > y) return0; if (x <= l && sm[p] <= y) { y -= sm[p]; return r - l + 1; } int m = (l + r) / 2; push(l, r, p); int ans = 0; if (x <= m) ans += query(x, y, lson); ans += query(x, y, rson); return ans; } intbounded(int v, int l, int r, int p){ if (mn[p] >= v) return r + 1; if (l == r) return l; int m = (l + r) / 2; if (mn[2 * p] >= v) return bounded(v, rson); return bounded(v, lson); } voidmodify(int x, int y){ int l = bounded(y, 1, n, 1); if (l <= x) rangeSet(l, x, y, 1, n, 1); } intquery(int x, int y){ return query(x, y, 1, n, 1); } };
// for a given covex function f: (f(a) + f(b)) / 2 >= f((a + b) / 2) int l = 1, r = 1e9; while (r > l) { int m = (r - l) / 3; int lm = l + m, rm = r - m; if (f(lm) < f(rm)) r = rm - 1; else l = lm + 1; } return l;
标准三分法用黄金分割的原因
我们不妨设原始区间为 [0, 1],我们在其中选两个点 0 < a < b < 1,然后比较 f(a) 和 f(b),然后再相应改变区间。然后重复上述过程。如果我们能充分利用计算过的值,也就是说假设更新后的区间为 [0, b] 那么我们自然想让 a 的计算值充分被利用,所以我们想再选的两个点的其中一个是 a,如果更新后区间为 [a, 1] 同理。也就是说我们有策略
化简可得 $b(1 + b) = 1$,即 $b = \frac{\sqrt{5} - 1}{2}, a = b ^ 2 = \frac{3 - \sqrt{5}}{2} = 1 - b$。
// 求每个长度为 m 的区间最大值的编号 std::vector<int> monicDequeMax(std::vector<int> &a, int m){ std::vector<int> r; std::deque<int> Q; for (int i = 0; i < a.size(); ++i) { if (!Q.empty() && i - Q.front() >= m) Q.pop_front(); // 如果求最小值,大于号改成小于号即可 while (!Q.empty() && a[i] > a[Q.back()]) Q.pop_back(); Q.push_back(i); // 如果需要返回值,就在下面加入 a[Q.front()] if (i >= m - 1) r.emplace_back(Q.front()); } return r; }
std::map<int, int> mp; intgetans(int n){ auto it = mp.find(n); if (it != mp.end()) return it->second; int r = LL(n) * (n - 1) % M * (n - 2) % M * inv3 % M; for (int i = 2, j; i <= n; i = j + 1) { j = n / (n / i); r -= LL(j - i + 1) * getans(n / i) % M; if (r < 0) r += M; } mp.insert(std::make_pair(n, r)); return r; }