跳转至

11 杂项

11.1 快读

char buf[1 << 20], *p1, *p2;
char gc() {
    return p1 == p2 ? p2 = buf + fread(p1 = buf, 1, 1 << 20, stdin), (p1 == p2) ? EOF : *p1++ : *p1++;
}
inline int read(int f = 1, char c = gc(), int x = 0) {
    while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = gc();
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc();
    return f * x;
}

11.2 调试

void pv(const T& v) {
    if constexpr (is_same_v<T, vector<int>>) {
        for (auto i : v) cout << i << " ";
    } else {
        cerr << v;
    }
}
#define debug(v) cerr << #v << " = ", pv(v), cerr << endl

11.3 __int128

#include <bits/stdc++.h>
using namespace std;

using i128 = __int128;

ostream& operator<<(ostream& os, i128 x) {
    if (x < 0) {
        os << "-";
        x = -x;
    }
    if (x == 0) return os << 0;
    string s;
    while (x) {
        s += char('0' + x % 10);
        x /= 10;
    }
    reverse(s.begin(), s.end());
    return os << s;
}

istream& operator>>(istream& is, i128& x) {
    string s; is >> s;
    x = 0;
    for (auto c : s) {
        x = x * 10 + c - '0';
    }
    return is;
}

i128 sqrti128(i128 x) {
    i128 lo = 0, hi = 1e18;
    while (lo < hi) {
        i128 mid = (lo + hi + 1) / 2;
        if (mid * mid <= x) {
            lo = mid;
        } else {
            hi = mid - 1;
        }
    }
    return lo;
}

11.4 实数域二分

for (int t = 1; t <= 100; t++) {
    ld mid = (l + r) / 2;
    if (judge(mid)) r = mid;
    else l = mid;
}
cout << l << endl;

11.5 整数域三分

这段代码求的是极小值,极大值需要改变不等号方向。

while (l < r) {
    int mid = (l + r) / 2;
    if (check(mid) <= check(mid + 1)) r = mid; // <========= 注意不等号方向
    else l = mid + 1;
}
cout << check(l) << endl;

11.6 实数域三分

ld l = -1E9, r = 1E9;
for (int t = 1; t <= 100; t++) {
    ld mid1 = (l * 2 + r) / 3;
    ld mid2 = (l + r * 2) / 3;
    if (judge(mid1) < judge(mid2)) {
        r = mid2;
    } else {
        l = mid1;
    }
}
cout << l << endl;

11.7 pb_ds

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

using namespace std;
using namespace __gnu_pbds;

// ordered_set: 支持 order_of_key / find_by_order
template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

int main() {
    // 普通有序集合
    ordered_set<int> os;
    os.insert(5);
    os.insert(1);
    os.insert(7);
    os.insert(3);
    // find_by_order(k): 返回第 k (0-based) 小元素的迭代器
    cout << "2nd: " << *os.find_by_order(2) << "\n"; // 5

    // order_of_key(x): 返回集合中严格小于 x 的元素个数
    cout << "count < 5: " << os.order_of_key(5) << "\n"; // 2 (1,3)

    // 删除
    os.erase(3);
    cout << "after erase 3, count < 5: " << os.order_of_key(5) << "\n"; // 1
}

11.8 日期换算(基姆拉尔森公式)

已知年月日,求星期数。

int week(int y,int m,int d){
    if(m<=2)m+=12,y--;
    return (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7+1;
}

11.9 自适应辛普森法(Simpson)

二维平面上的曲线长度 $$ L = ∫_a^b \sqrt{1 + (f'(x))^2} dx $$

\[ L = ∫_{t1}^{t2} \sqrt{(dx/dt)^2 + (dy/dt)^2} dt \]
const int MAX_DEPTH = 60;

double simpson(double l, double r) {
    return (f(l) + 4.0 * f((l + r) / 2.0) + f(r)) * (r - l) / 6.0;
}

double integral_rec(double l, double r, double eps, double st, int depth) {
    double mid = (l + r) / 2.0;
    double sl = simpson(l, mid), sr = simpson(mid, r);
    double I = sl + sr;
    double err = fabs(I - st) / 15.0; // 估计的误差

    // 复合绝对/相对容差
    double tol = max(eps, eps * fabs(I)); // 或:max(abs_tol, rel_tol * fabs(I))

    if (err <= tol || depth >= MAX_DEPTH) {
        return I + (I - st) / 15.0; // Richardson 修正
    }
    return integral_rec(l, mid, eps / 2.0, sl, depth + 1)
         + integral_rec(mid, r, eps / 2.0, sr, depth + 1);
}

double integral(double l, double r, double EPS) {
    double s = simpson(l, r);
    return integral_rec(l, r, EPS, s, 0);
}

11.10 整数除法

int floordiv(int x, int y) {
    int q = x / y;
    if (x % y and (x > 0) != (y > 0)) q--;
    return q;
}
int ceildiv(int x, int y) {
    int q = x / y;
    if (x % y and (x > 0) == (y > 0)) q++;
    return q;
}