XCPC模板

!!!声明:自用模板,使用前请自测,比赛中出现bug概不负责。

一、杂项

__int128_t重载

using i128 = __int128_t;
std::ostream &operator<<(std::ostream &os, i128 n) {
    if (n == 0) {
        return os << 0;
    }
    std::string s;
    while (n > 0) {
        s += char('0' + n % 10);
        n /= 10;
    }
    std::reverse(s.begin(), s.end());
    return os << s;
}

二分整数

while (l < r) { // 左边界
    int mid = l + r >> 1;
    if (check(mid)) {
        r = mid;
    } else {
        l = mid + 1;
    }
}
while (l < r) { // 右边界
    int mid = l + r + 1 >> 1;
    if (check(mid)) {
        l = mid;
    } else {
        r = mid - 1;
    }
}

二分实数

using real = long double;
constexpr real eps = 1E-7;
while (r - l > std::max(1.0, l) * eps) {
    double x = (l + r) / 2;
    if (check(x)) {
        r = x;
    } else {
        l = x;
    }
}

DEBUG

template <class T, size_t size = std::tuple_size<T>::value> std::string to_debug(T, std::string s = "") requires(not std::ranges::range<T>);
std::string to_debug(auto x) requires requires(std::ostream& os) { os << x; } { return static_cast<std::ostringstream>(std::ostringstream() << x).str(); }
std::string to_debug(std::ranges::range auto x, std::string s = "") requires(not std::is_same_v<decltype(x), std::string>) {
  for (auto xi : x) { s += ", " + to_debug(xi); }
  return "[" + s.substr(s.empty() ? 0 : 2) + "]";
}
template <class T, size_t size> std::string to_debug(T x, std::string s) requires(not std::ranges::range<T>) {
  [&]<size_t... I>(std::index_sequence<I...>) { ((s += ", " + to_debug(get<I>(x))), ...); }(std::make_index_sequence<size>());
  return "(" + s.substr(s.empty() ? 0 : 2) + ")";
}
#define debug(...) std::cerr << __LINE__ << ": (" #__VA_ARGS__ ") = " << to_debug(std::tuple(__VA_ARGS__)) << "\n"

二、图

树链剖分(HLD)

点击展开
using Edge = int;
struct HLD { // 下标从1开始
	int n, times = 0;
	std::vector<int> siz, top, dep, fa, in, out, seq;
	std::vector<std::vector<Edge>> adj;
	HLD(const auto &adj, int root = 1) : n((int)adj.size() - 1), adj(adj) {
		siz.resize(n + 1), top.resize(n + 1), dep.resize(n + 1), in.resize(n + 1), out.resize(n + 1), seq.resize(n + 1), fa.resize(n + 1);
		dep[root] = 1, top[root] = root;
		dfsSiz(root), dfsHld(root);
    }
	void dfsSiz(int u) {
		if (fa[u] != 0) {
			adj[u].erase(std::find(adj[u].begin(), adj[u].end(), fa[u]));
		}
		siz[u] = 1;
		for (auto &v : adj[u]) {
			fa[v] = u;
			dep[v] = dep[u] + 1;
			dfsSiz(v);
			siz[u] += siz[v];
			if (siz[v] > siz[adj[u][0]]) {
                std::swap(v, adj[u][0]);
            }
		}
	}
	void dfsHld(int u) {
		in[u] = ++times;
		seq[in[u]] = u;
		for (auto v : adj[u]) {
			top[v] = v == adj[u][0] ? top[u] : v;
			dfsHld(v);
		}	
		out[u] = times;
	}
	int lca(int u, int v) {
		while (top[u] != top[v]) {
            dep[top[u]] > dep[top[v]] ? u = fa[top[u]] : v = fa[top[v]];
        }
		return dep[u] < dep[v] ? u : v;
	}
	int rootedLca(int a, int b, int c) { return lca(a, b) ^ lca(b, c) ^ lca(a, c); }
	int dist(int u, int v) { return dep[u] + dep[v] - 2 * dep[(lca(u, v))]; }
	bool inSubtree(int u, int v) { return in[v] <= in[u] && in[u] <= out[v]; }
	int jump(int u, int k) {
		if (dep[u] < k) return -1;
		int d = dep[u] - k;
		while (dep[top[u]] - d && u) u = fa[top[u]];
		return seq[in[u] - dep[u] + d];
	}
	
	template<typename Q>
	void modifyPath(int u, int v, const Q &q, bool edge = false) {
		while (top[u] != top[v]) {
			if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
			q(in[top[u]], in[u]);
			u = fa[top[u]];
		}
		if (dep[u] > dep[v]) std::swap(u, v);
		q(in[u] + edge, in[v]);
	}
	
	template<typename Q>
	void modifySubtree(int u, const Q &q) { q(in[u], out[u]); }

	template<typename T, typename Q>
	T queryPath(int u, int v, const Q &q, bool edge = false) {
		T ret = T();
		while (top[u] != top[v]) {
			if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
			ret = q(in[top[u]], in[u]) + ret;
			u = fa[top[u]];
		}
		if (dep[u] > dep[v]) std::swap(u, v);
		return q(in[u] + edge, in[v]) + ret;
	}

	template<typename T, typename Q>
	T querySubtree(int u, const Q &q) { return q(in[u], out[u]); }

	template<typename T, typename Q, typename F>
    T queryPathNoncommutative(int u, int v, const Q &q, const F &f, bool edge = false) {
        T left = T(), right = T();
        while(top[u] != top[v]) {
            if (dep[top[u]] < dep[top[v]]) std::swap(u, v), std::swap(left, right);
            left = q(in[top[u]], in[u]) + left;
            u = fa[top[u]];
        }
        if (dep[u] > dep[v]) std::swap(u, v), std::swap(left, right);
        return f(left, q(in[u] + edge, in[v]) + right);
    }

	template<typename T, typename Q>
    T queryPathDirection(int u, int v, const Q &q, bool edge = false) {
        T left = T(), right = T();
        while(top[u] != top[v]) {
			if (dep[top[u]] < dep[top[v]]) {
				right = q(in[top[v]], in[v]) + right;
				v = fa[top[v]];
			} else {
				left = q(in[top[u]], in[u]) + left;
				u = fa[top[u]];
			}
        }
        if (dep[u] > dep[v]) {
			left = q(in[v], in[u]) + left;
		} else {
			right = q(in[u], in[v]) + right;
		}
		std::swap(left.lmx, left.rmx);
		return left + right;
    }

	std::pair<std::unordered_map<int, std::vector<int>>, int> virtualTree(std::vector<int> v) {
        auto cmp = [&](int a, int b) { return in[a] < in[b]; };
        std::sort(v.begin(), v.end(), cmp);
        v.erase(std::unique(v.begin(), v.end()), v.end());
        const int k = (int)size(v);
        for (int i = 0; i + 1 < k; ++i) {
            v.push_back(lca(v[i], v[i + 1]));
        }
        std::sort(v.begin(), v.end(), cmp);
        v.erase(std::unique(v.begin(), v.end()), v.end());
        std::unordered_map<int, std::vector<int>> res;
        std::vector<int> stk;
        for (auto x : v) {
            while (!stk.empty() && out[stk.back()] < in[x]) {
                stk.pop_back();
            }
            if (!stk.empty()) {
                res[stk.back()].push_back(x);
            }
            stk.push_back(x);
        }
        return {res, v[0]};
    }

	std::pair<std::vector<int>, std::vector<std::pair<int, int>>> compress(std::vector<int> v) {
        auto cmp = [&](int a, int b) { return in[a] < in[b]; };
        std::sort(v.begin(), v.end(), cmp);
        v.erase(std::unique(v.begin(), v.end()), v.end());
        const int k = (int)size(v);
        for (int i = 0; i + 1 < k; ++i) {
            v.push_back(lca(v[i], v[i + 1]));
        }
        std::sort(v.begin(), v.end(), cmp);
        v.erase(std::unique(v.begin(), v.end()), v.end());
        std::vector<std::pair<int, int> > edges;
        std::vector<int> stk;
        for (auto x : v) {
            while (!stk.empty() && out[stk.back()] < in[x]) {
                stk.pop_back();
            }
            if (!stk.empty()) {
                edges.push_back({stk.back(), x});
            }
            stk.push_back(x);
        }
        return {v, edges};
    }
};

强联通分量(SCC)

点击展开
struct SCC {
    int n;
    std::vector<std::vector<int>> adj;
    std::vector<int> stk;
    std::vector<int> dfn, low, bel;
    int cur, cnt;
    SCC() {}
    SCC(int n) { init(n); }
    void init(int n) {
        this->n = n;
        adj.assign(n, {});
        dfn.assign(n, -1), low.resize(n), bel.assign(n, -1);
        stk.clear();
        cur = cnt = 0;
    }
    void addEdge(int u, int v) { adj[u].push_back(v); }
    void tarjan(int u) {
        dfn[u] = low[u] = cur++;
        stk.push_back(u);
        for (auto v : adj[u]) {
            if (dfn[v] == -1) {
                tarjan(v);
                low[u] = std::min(low[u], low[v]);
            } else if (bel[v] == -1) {
                low[u] = std::min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) {
            int y;
            do {
                y = stk.back();
                bel[y] = cnt;
                stk.pop_back();
            } while (y != u);
            cnt += 1;
        }
    }
    std::vector<int> work() {
        for (int i = 0; i < n; ++i) {
            if (dfn[i] == -1) {
                tarjan(i);
            }
        }
        return bel;
    }
    
    struct Graph {
        int n;
        std::vector<std::pair<int, int>> edges;
        std::vector<int> siz;
        std::vector<int> cnte;
    };
    Graph compress() {
        Graph g;
        g.n = cnt;
        g.siz.resize(cnt);
        g.cnte.resize(cnt);
        for (int i = 0; i < n; ++i) {
            g.siz[bel[i]]++;
            for (auto j : adj[i]) {
                if (bel[i] != bel[j]) {
                    g.edges.emplace_back(bel[i], bel[j]);
                    g.cnte[bel[j]]++;
                }
            }
        }
        return g;
    }
};

边双联通分量(EDCC)

点击展开
struct EDCC {
    int n, times = 0, top = 0, dcc_cnt = 0, idx = 0;
    std::vector<std::vector<int>> adj, dcc;
    std::vector<int> isb, dfn, low, stk, d, id, h, e, ne;
    EDCC () {}
    EDCC(int n, int m) : n(n), isb(m), dfn(n), low(n), stk(n + 10), id(n), h(n, -1), e(m), ne(m), d(n + 10), dcc(n + 10) {}
    void add(int a, int b) {
        e[idx] = b, ne[idx] = h[a], h[a] = idx++;
        e[idx] = a, ne[idx] = h[b], h[b] = idx++;
    }
    void tarjan(int u, int from) {
        dfn[u] = low[u] = ++ times;
        stk[ ++ top] = u;
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if (!dfn[j]) {
                tarjan(j, i);   
                low[u] = min(low[u], low[j]);
                if (dfn[u] < low[j]) isb [i] = isb[i ^ 1] = true;
            } else if (i != (from ^ 1)) low[u] = min(low[u], dfn[j]);
        }
        if (dfn[u] == low[u]) {
            int y; 
            dcc_cnt ++ ;
            do {
                y = stk[top -- ];
                id[y] = dcc_cnt;
                dcc[dcc_cnt].push_back(y);
            } while (y != u);
        }
    }
    void build() {
        tarjan(1, -1);
        for (int i = 0; i < idx; ++ i)
            if (isb[i]) d[id[e[i]]] ++ ;
    }
};

点双联通分量(VDCC)

点击展开
struct VDCC {
    int n, top = 0, times = 0, dcc_cnt = 0, root;
    std::vector<std::vector<int>> adj, dcc;
    std::vector<int> dfn, low, stk;
    std::vector<bool> cut;
    VDCC(int n) : n(n), adj(n), dcc(n + 10), dfn(n), low(n), stk(n + 10), cut(n) {}
    void add(int a, int b) { adj[a].push_back(b), adj[b].push_back(a); }
    void tarjan(int u) {
        dfn[u] = low[u] = ++times;
        stk[ ++ top] = u;
        if (u == root && adj[u].size() == 0) return void(dcc[ ++ dcc_cnt].push_back(u)); // 如果u是孤点
        int cnt = 0;
        for (auto j : adj[u])
            if (!dfn[j]) {
                tarjan(j);
                low[u] = min(low[u], low[j]);
                if (dfn[u] <= low[j]) {
                    int y;
                    ++ dcc_cnt, ++ cnt;
                    if (u != root || cnt > 1) cut[u] = true; // 判断是否为割点, 如果u是根节点则至少需要两条边
                    do {
                        y = stk[top -- ];
                        dcc[dcc_cnt].push_back(y);
                    } while (y != j);
                    dcc[dcc_cnt].push_back(u);
                }
            } else low[u] = min(low[u], dfn[j]);
    }
    void build() {
        for (root = 0; root < n; ++ root)
            if (!dfn[root]) tarjan(root);
    }
};

三、数据结构

并查集(DSU)

struct DSU {
    std::vector<int> p, siz;
    DSU(int n) { init(n); }
    void init(int n) {
        p.resize(n);
        std::iota(p.begin(), p.end(), 0);
        siz.assign(n, 1);
    }
    int leader(int x) {
        while (x != p[x]) x = p[x] = p[p[x]];
        return x;
    }
    bool same(int x, int y) { return leader(x) == leader(y); }
    bool merge(int x, int y) {
        x = leader(x), y = leader(y);
        if (x == y) return false;
        siz[x] += siz[y], p[y] = x;
        return true;
    }
    int size(int x) { return siz[leader(x)]; }
};

可撤销并查集(DSU with Rollback)

struct DSU {
    std::vector<int> p, siz;
    std::vector<std::array<int, 2>> his; 
    DSU(int n) { init(n); }
    void init(int n) {
        p.resize(n);
        std::iota(p.begin(), p.end(), 0);
        siz.assign(n, 1);
    }
    int leader(int x) {
        while (x != p[x]) x = p[x] = p[p[x]];
        return x;
    }
    bool same(int x, int y) { return leader(x) == leader(y); }
    bool merge(int x, int y) {
        x = leader(x), y = leader(y);
        if (x == y) return false;
        his.push_back({x, y});
        siz[x] += siz[y], p[y] = x;
        return true;
    }
    int time() { return size(his;) }
    int size(int x) { return siz[leader(x)]; }
    void revert(int tm) {
        while (size(his) > tm) {
            auto [x, y] = his.back();
            his.pop_back();
            f[y] = y;
            siz[x] -= siz[y];
        }
    }
};

树状数组(Fenwick)

template<typename T>
struct Fenwick {
    const int n;
    std::vector<T> tr;
    Fenwick(int n) : n(n), tr(n + 1) {}
    void add(int x, T c) {
        for (int i = x; i <= n; i += i & -i) tr[i] += c;
    }
    void add(int l, int r, T c) {
        add(l, c);
        if (r + 1 <= n) add(r + 1, -c);
    }
    T get(int x) {
        T res = T();
        for (int i = x; i > 0; i -= i & -i) res += tr[i];
        return res;
    }
    T get(int l, int r) {
        return get(r) - get(l - 1);
    }
    int find_first(T sum) {
        int ans = 0; T val = 0;
        for (int i = std::__lg(n); i >= 0; --i) {
            if ((ans | (1 << i)) <= n and val + tr[ans | (1 << i)] < sum) {
                ans |= 1 << i;
                val += tr[ans];
            }
        }
        return ans + 1;
    }
    int find_last(T sum) {
        int ans = 0; T val = 0;
        for (int i = std::__lg(n); i >= 0; --i) {
            if ((ans | (1 << i)) <= n and val + tr[ans | (1 << i)] <= sum) {
                ans |= 1 << i;
                val += tr[ans];
            }
        }
        return ans;
    }
};
using BIT = Fenwick<int>;

单点修改线段树(SegTree)

点击展开
template<class Info>
struct SegTree { // 下标从1开始
    const int n;
    std::vector<Info> tr;
    SegTree(int n) : n(n), tr(4 << (std::__lg(n))) {}
    SegTree(int n, const std::vector<Info> &a) : SegTree(n) {
        std::function<void(int, int, int)> build = [&](int u, int l, int r) {
            if (l == r) return void(tr[u] = a[r]);
            int mid = l + r >> 1;
            build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
            pushup(u);
        };
        build(1, 1, n);
    }
    void pushup(int u) { tr[u] = tr[u << 1] + tr[u << 1 | 1]; }
    void modify(int u, int l, int r, int x, const Info &v) {
        if (l == r) return void(tr[u] = v);
        int mid = l + r >> 1;
        if (x <= mid) modify(u << 1, l, mid, x, v);
        else modify(u << 1 | 1, mid + 1, r, x, v);
        pushup(u);
    }
    Info query(int u, int l, int r, int ql, int qr) {
        if (l > qr || r < ql) return Info();
        if (ql <= l && r <= qr) return tr[u];
        int mid = l + r >> 1;
        return query(u << 1, l, mid, ql, qr) + query(u << 1 | 1, mid + 1, r, ql, qr);
    }
    void modify(int x, const Info &v) { modify(1, 1, n, x, v); }
    Info query(int l, int r) { return query(1, 1, n, l, r);}
};
struct Info {};
Info operator+(const Info &l, const Info &r) {
    return {};
}

懒标记线段树(LazySegTree)

点击展开
template<class Info, class Tag>
struct LazySegTree { // 下标从1开始
    const int n;
    std::vector<Tag> tag;
    std::vector<Info> tr;
    LazySegTree(int n) : n(n), tr(4 << std::__lg(n)), tag(4 << std::__lg(n)) {}
    LazySegTree(const std::vector<Info> &a) : LazySegTree((int)a.size()) {
        std::function<void(int, int, int)> build = [&](int u, int l, int r) {
            if (l == r) return void(tr[u] = a[r - 1]);
            int mid = l + r >> 1;
            build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
            pushup(u);
        };
        build(1, 1, n);
    }
    void pushup(int u) { tr[u] = tr[u << 1] + tr[u << 1 | 1]; }
    void apply(int u, const Tag &v) { tr[u].apply(v), tag[u].apply(v); }
    void pushdown(int u) {
        apply(u << 1, tag[u]), apply(u << 1 | 1, tag[u]);
        tag[u] = Tag();
    }
    void modify(int u, int l, int r, int x, const Info &v) {
        if (l == r) return void(tr[u] = v);
        int mid = l + r >> 1;
        if (x <= mid) modify(u << 1, l, mid, x, v);
        else modify(u << 1 | 1, mid + 1, r, x, v);
        pushup(u);
    }
    void modify(int x, const Info &v) { modify(1, 1, n, x, v); }
    Info query(int u, int l, int r, int ql, int qr) {
        if (l > qr || r < ql) return Info();
        if (ql <= l && r <= qr) return tr[u];
        int mid = l + r >> 1;
        pushdown(u);
        return query(u << 1, l, mid, ql, qr) + query(u << 1 | 1, mid + 1, r, ql, qr);
    }
    Info query(int l, int r) { return query(1, 1, n, l, r);}
    void rangeApply(int u, int l, int r, int ql, int qr, const Tag &v) {
        if (l > qr || r < ql) return ;
        if (l >= ql && r <= qr) { return void(apply(u, v)); }
        int mid = l + r >> 1;
        pushdown(u);
        rangeApply(u << 1, l, mid, ql, qr, v), rangeApply(u << 1 | 1, mid + 1, r, ql, qr, v);
        pushup(u);
    }
    void rangeApply(int l, int r, const Tag &v) { return rangeApply(1, 1, n, l, r, v); }
};
struct Tag {
    void apply(const Tag &t) {}
};
struct Info {
    int n;
    void apply(const Tag &t) {}
};
Info operator+(const Info &l, const Info &r) {
    return {};
}

状压RMQ

点击展开
template<class T,
    class Cmp = std::less<T>>
struct RMQ {
    const Cmp cmp = Cmp();
    static constexpr unsigned B = 64;
    using u64 = unsigned long long;
    int n;
    std::vector<std::vector<T>> a;
    std::vector<T> pre, suf, ini;
    std::vector<u64> stk;
    RMQ() {}
    RMQ(const std::vector<T> &v) {
        init(v);
    }
    void init(const std::vector<T> &v) {
        n = v.size();
        pre = suf = ini = v;
        stk.resize(n);
        if (!n) return;
        const int M = (n - 1) / B + 1;
        const int lg = std::__lg(M);
        a.assign(lg + 1, std::vector<T>(M));
        for (int i = 0; i < M; ++i) {
            a[0][i] = v[i * B];
            for (int j = 1; j < B && i * B + j < n; ++j) {
                a[0][i] = std::min(a[0][i], v[i * B + j], cmp);
            }
        }
        for (int i = 1; i < n; ++i) {
            if (i % B) {
                pre[i] = std::min(pre[i], pre[i - 1], cmp);
            }
        }
        for (int i = n - 2; i >= 0; --i) {
            if (i % B != B - 1) {
                suf[i] = std::min(suf[i], suf[i + 1], cmp);
            }
        }
        for (int j = 0; j < lg; ++j) {
            for (int i = 0; i + (2 << j) <= M; ++i) {
                a[j + 1][i] = std::min(a[j][i], a[j][i + (1 << j)], cmp);
            }
        }
        for (int i = 0; i < M; ++i) {
            const int l = i * B;
            const int r = std::min(1U * n, l + B);
            u64 s = 0;
            for (int j = l; j < r; ++j) {
                while (s && cmp(v[j], v[std::__lg(s) + l])) {
                    s ^= 1ULL << std::__lg(s);
                }
                s |= 1ULL << (j - l);
                stk[j] = s;
            }
        }
    } 
    T operator()(int l, int r) { // [l, r)
        if (l / B != (r - 1) / B) {
            T ans = std::min(suf[l], pre[r - 1], cmp);
            l = l / B + 1;
            r = r / B;
            if (l < r) {
                int k = std::__lg(r - l);
                ans = std::min({ans, a[k][l], a[k][r - (1 << k)]}, cmp);
            }
            return ans;
        } else {
            int x = B * (l / B);
            return ini[__builtin_ctzll(stk[r - 1] >> (l - x)) + l];
        }
    }
};

ST表

struct ST {
    std::vector<std::vector<int>> f, g, h;
    ST(int n, std::vector<int>& a) : f(n, std::vector<int>(std::__lg(n) + 1)), 
        g(n, std::vector<int>(std::__lg(n) + 1)), h(n, std::vector<int>(std::__lg(n) + 1)) {
        for (int i = 1; i < n; i ++) {
            f[i][0] = g[i][0] = h[i][0] = a[i];
        }
        for (int j = 1; (1LL << j) < n; j ++) {
            for (int i = 1; i + (1LL << j) - 1 < n; i ++) {
                f[i][j] = std::max(f[i][j - 1], f[i + (1LL << (j - 1))][j - 1]);
                g[i][j] = std::min(g[i][j - 1], g[i + (1LL << (j - 1))][j - 1]);
                h[i][j] = std::__gcd(h[i][j - 1], h[i + (1LL << (j - 1))][j - 1]);
            }
        }
    }
    int askMax(int l, int r) {
        int k = std::__lg(r - l + 1);
        return std::max(f[l][k], f[r - (1LL << k) + 1][k]);
    }
    int askMin(int l, int r) {
        int k = std::__lg(r - l + 1);
        return std::min(g[l][k], g[r - (1LL << k) + 1][k]);
    }
    int askGcd(int l, int r) {
        int k = std::__lg(r - l + 1);
        return std::__gcd(h[l][k], h[r - (1LL << k) + 1][k]);
    }
};

高精度整数-不含负数(BigInt)

点击展开
struct BigInt {
    std::vector<char> v;
    BigInt(int x = 0) : v{} {
        do v.push_back(x % 10); while (x /= 10);
    }
    BigInt(std::string &x) : v{} {
        assert(size(x) != 0);
        for (int i = (int)size(x) - 1; i >= 0; --i) {
            v.push_back(x[i] - '0');
        }
    }
    BigInt &operator=(int x) {
        v.clear();
        do v.push_back(x % 10); while (x /= 10);
        return *this;
    }
    BigInt &operator=(const BigInt &x) {
        v.resize(x.v.size());
        memcpy(const_cast<char *>(v.data()), x.v.data(), x.v.size() * sizeof(char));
        return *this;
    }
    friend bool operator==(const BigInt &a, const BigInt &b) {
        if (size(a.v) == size(b.v)) {
            int idx = 0;
            while (idx < (int)size(a.v) && a.v[idx] == b.v[idx]) idx++; 
            return idx < (int)size(a.v);
        } else {
            return false;
        }
    }
    friend bool operator!=(const BigInt &a, const BigInt &b) {
        return !(a == b);
    }
    friend bool operator<(const BigInt &a, const BigInt &b) {
        if (size(a.v) == size(b.v)) {
            int idx = size(a.v) - 1;
            while (idx >= 0 && a.v[idx] == b.v[idx]) idx--; 
            return idx >= 0 && a.v[idx] < b.v[idx];
        }
        return size(a.v) < size(b.v);
    }
    friend bool operator<=(const BigInt &a, const BigInt &b) {
        if (size(a.v) == size(b.v)) {
            int idx = size(a.v) - 1;
            while (idx >= 0 && a.v[idx] == b.v[idx]) idx--; 
            return idx == -1 || a.v[idx] <= b.v[idx];
        }
        return size(a.v) < size(b.v);
    }
    friend bool operator>(const BigInt &a, const BigInt &b) {
        if (size(a.v) == size(b.v)) {
            int idx = size(a.v) - 1;
            while (idx >= 0 && a.v[idx] == b.v[idx]) idx--; 
            return idx >= 0 && a.v[idx] > b.v[idx];
        }
        return size(a.v) > size(b.v);
    }
    friend bool operator>=(const BigInt &a, const BigInt &b) {
        if (size(a.v) == size(b.v)) {
            int idx = size(a.v);
            while (idx >= 0 && a.v[idx] == b.v[idx]) idx--;
            return idx == -1 || a.v[idx] >= b.v[idx];
        }
        return size(a.v) > size(b.v);
    }
    BigInt &operator+=(const BigInt &x) & {
        int n = std::max<int>(size(v), size(x.v)), tmp = 0;
        bool flag = false;
        for (int i = 0; i < n; ++i) {
            if (i >= (int)size(v)) v.push_back(0);
            if (i < (int)size(x.v)) v[i] += x.v[i];
            if (flag) v[i] += 1, flag = false;
            if (v[i] >= 10) v[i] %= 10, flag = true;
        }
        if (flag) v.push_back(1);
        return *this;
    }
    BigInt &operator-=(const BigInt &x) & {
        assert(*this >= x);
        bool flag = false;
        for (int i = 0; i < (int)size(v); ++i) {
            if (i < (int)size(x.v)) v[i] -= x.v[i];
            if (flag) v[i] -= 1, flag = false;
            if (v[i] < 0) v[i] += 10, flag = true;
        }
        while (size(v) > 1 && v.back() == 0) v.pop_back();
        return *this;
    }
    BigInt &operator*=(const int &x) & {
        int tmp = 0;
        for (int i = 0; i < size(v); ++i) {
            tmp += (int)x * v[i];
            v[i] = tmp % 10;
            tmp /= 10;
        }
        while (tmp) {
            v.push_back(tmp % 10);
            tmp /= 10;
        }
        return *this;
    }
    BigInt &operator*=(const BigInt &x) & {
        BigInt result;
        result.v.resize(size(v) + size(x.v));
        for (int i = 0; i < (int)size(v); ++i) {
            for (int j = 0; j < (int)size(x.v); ++j) {
                result.v[i + j] += v[i] * x.v[j];
                result.v[i + j + 1] += result.v[i + j] / 10;
                result.v[i + j] %= 10;   
            }
        }
        while (size(result.v) > 1 && result.v.back() == 0) result.v.pop_back();
        return *this = result;
    }
    BigInt &operator/=(const int &x) & {
        int r = 0;
        for (int i = size(v) - 1; i >= 0; --i) {
            r = r * 10 + v[i];
            v[i] = r / x;
            r %= x;
        }
        while (size(v) > 1 && v.back() == 0) v.pop_back();
        return *this;
    }
    int operator%=(const int &x) {
        int r = 0;
        for (int i = size(v) - 1; i >= 0; --i) {
            r = r * 10 + v[i];
            r %= x;
        }
        return r;
    }
    friend BigInt operator+(BigInt a, const BigInt &b) {
        return a += b;
    }
    friend BigInt operator-(BigInt a, const BigInt &b) {
        return a -= b;
    }
    friend BigInt operator*(BigInt a, const int &b) {
        return a *= b;
    }
    friend BigInt operator*(BigInt a, const BigInt &b) {
        return a *= b;
    }
    friend BigInt operator/(BigInt a, const int &b) {
        return a /= b;
    }
    friend int operator%(BigInt a, const int &b) {
        return a %= b;
    }
    friend std::istream &operator>>(std::istream &is, BigInt &a) {
        std::string str;
        is >> str;
        a = BigInt(str);
        return is;
    }
    friend std::ostream &operator<<(std::ostream &os, const BigInt &a) {
        for (int i = a.v.size() - 1; i >= 0; --i) os << (char)(a.v[i] + '0');
        return os;
    }
};

四、数学,几何

快速幂(power)

int power(int a, int b, int p) {
    int c = 1;
    while (b) [
        if (b & 1) {
            c = 1LL * c * a % p;
        }
        a = 1LL * a * a % p;
        b >>= 1;
    ]
    return c;
}

欧拉筛

std::vector<int> primes, least;
void sieve(int n) {
    std::vector<int> nums;
    least.assign(n + 1, 0);
    for (int i = 2; i <= n; ++i) {
        if (least[i] == 0) {
            least[i] = i;
            nums.push_back(i);
        }
        for (auto p : primes) {
            if (i * p > n) {
                break;
            }
            least[i * p] = p;
            if (p == least[i]) {
                break;
            }
        }
    }
    primes = nums;
}

bool isprime(int n) {
    return least[n] == n;
}

筛欧拉函数

std::vector<int> primes, phi, least;
void sieve(int n) {
    least.assign(n + 1, 0);
    phi.assign(n + 1, 0);
    primes.clear();
    for (int i = 2; i <= n; ++i) {
        if (least[i] == 0) {
            least[i] = i;
            phi[i] = i - 1;
            primes.push_back(i);
        }
        for (auto p : primes) {
            if (i * p > n) {
                break;
            }
            least[i * p] = p;
            if (p == least[i]) {
                phi[i * p] = phi[i] * p;
                break;
            }
            phi[i * p] = phi[i] * (p - 1);
        }
    }
    for (int i = 2; i <= n; ++i) {
        phi[i] += phi[i - 1];
    }
}

扩展欧几里得(exgcd)

int exgcd(int a, int b, int &x, int &y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    int g = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return g;
}

// ax + b = 0 (mod m)
std::pair<int, int> sol(int a, int b, int m) {
    assert(m > 0);
    b *= -1;
    int x, y;
    int g = exgcd(a, m, x, y);
    if (g < 0) {
        g *= -1;
        x *= -1;
        y *= -1;
    }
    if (b % g != 0) {
        return {-1, -1};
    }
    x = x * (b / g) % (m / g);
    if (x < 0) {
        x += m / g;
    }
    return {x, m / g};
}

std::array<int, 3> exgcd(int a, int b) {
    if (!b) {
        return {a, 1, 0};
    }
    auto [g, x, y] = exgcd(b, a % b);
    return {g, y, x - a / b * y};
}

素性测试(Factorizer)

点击展开
std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
namespace Factorizer {
    std::vector<int> primes;
    std::vector<int> least;
    void sieve(int n) {
        std::vector<int> nums;
        least.assign(n + 1, 0);
        for (int i = 2; i <= n; ++i) {
            if (least[i] == 0) {
                least[i] = i;
                nums.push_back(i);
            }
            for (auto p : nums) {
                if (i * p > n) {
                    break;
                }
                least[i * p] = p;
                if (p == least[i]) {
                    break;
                }
            }
        }
        primes = nums;
    }
    constexpr bool miller_rabin(long long n) {
        if (n <= 1 || (n != 2 && n % 2 == 0)) return false;
        for (auto a : {3, 5, 7, 11, 13, 17, 19, 23, 29}) {
            if (n % a == 0) return n == a;
        }
        if (n < 31 * 31) return true;
        long long d = n - 1;
        while (d % 2 == 0) d /= 2;
        constexpr long long bases[] = {
            2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37
        };
        for (long long a : bases) {
            if (n == a) return true;
            long long t = d;
            long long y = 1 % n;
            for (long long _t = t; _t != 0; _t >>= 1) {
                if (_t & 1) y = (__int128) y * a % n;
                a = (__int128) a * a % n;
            }
            while (t != n - 1 && y != 1 && y != n - 1) {
                y = (__int128) y * y % n;
                t <<= 1;
            }
            if (y != n - 1 && t % 2 == 0) return false;
        }
        return true;
    }
    long long pollard_rho(long long n) {
        if (n == 1 || miller_rabin(n)) return n;
        long long now = 0;
        do {
            long long t = std::gcd(++now, n), r = t;
            if (t != 1 && t != n) return t;
            long long g = 1;
            do {
                t = ((__int128) t * t % n + now) % n;
                r = ((__int128) r * r % n + now) % n;
                r = ((__int128) r * r % n + now) % n;
            } while ((g = std::gcd(abs(t - r), n)) == 1);
            if (g != n) return g;
        } while (now < n / now);
        return 0;
    }
    std::vector<long long> factor(long long n) {
        if (n == 1) return {};
        std::vector<long long> g, d;
        d.push_back(n);
        while (!d.empty()) {
            auto v = d.back();
            d.pop_back();
            auto rho = pollard_rho(v);
            if (rho == v) {
                g.push_back(rho);
            } else {
                d.push_back(rho);
                d.push_back(v / rho);
            }
        }
        std::sort(g.begin(), g.end());
        return g;
    }
}

取模类(Mint)

点击展开
using i64 = long long;

template <class T>
constexpr T power(T a, i64 b) {
    T res = 1;
    for (; b; b /= 2, a *= a)
        if (b % 2) res *= a;
    return res;
}
template <int P>
struct MInt {
    int x;
    constexpr MInt() : x{} {}
    constexpr MInt(i64 x) : x{norm(x % P)} {}
    constexpr int norm(int x) const {
        if (x < 0) x += P;
        if (x >= P) x -= P;
        return x;
    }
    constexpr int val() const { return x; }
    explicit constexpr operator int() const { return x; }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(P - x);
        return res;
    }
    constexpr MInt inv() const {
        assert(x != 0);
        return power(*this, P - 2);
    }
    constexpr MInt &operator*=(MInt rhs) {
        x = 1ll * x * rhs.x % P;
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) { return *this *= rhs.inv(); }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
        i64 v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) { return os << a.val(); }
    friend constexpr bool operator==(MInt lhs, MInt rhs) { return lhs.val() == rhs.val(); }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) { return lhs.val() != rhs.val(); }
};

template <int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();

constexpr int P = 998244353;
//constexpr int P = 1e9 + 7;
using Z = MInt<P>;

组合数(Comb,取模Z)

点击展开
struct Comb {
    int n;
    std::vector<Z> _fac, _invfac, _inv;
    Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}
    Comb(int n) : Comb() { init(n); }
    void init(int m) {
        if (m <= n) return;
        _fac.resize(m + 1), _invfac.resize(m + 1), _inv.resize(m + 1);
        for (int i = n + 1; i <= m; ++i) _fac[i] = _fac[i - 1] * i;
        _invfac[m] = _fac[m].inv();
        for (int i = m; i > n; --i) _invfac[i - 1] = _invfac[i] * i, _inv[i] = _invfac[i] * _fac[i - 1];
        n = m;
    }
    Z fac(int m) {
        if (m > n) init(2 * m);
        return _fac[m];
    }
    Z invfac(int m) {
        if (m > n) init(2 * m);
        return _invfac[m];
    }
    Z inv(int m) {
        if (m > n) init(2 * m);
        return _inv[m];
    }
    Z binom(int n, int m) {
        if (n < m || m < 0) return 0;
        return fac(n) * invfac(m) * invfac(n - m);
    }
} comb;

组合数(无Z)

constexpr int P = 1e9 + 7;
constexpr int L = 1e6 + 10;

int fac[L + 1], invfac[L + 1];

int binom(int n, int m) {
    if (n < m || m < 0) {
        return 0;
    }
    return 1LL * fac[n] * invfac[m] % P * invfac[n - m] % P;
}

int power(int a, int b) {
    int c = 1;
    while (b) {
        if (b & 1) {
            c = 1LL * c * a % P;
        }
        a = 1LL * a * a % P;
        b >>= 1;
    }
    return c;
}

void combInit() {
    fac[0] = 1;
    for (int i = 1; i <= L; ++i) {
        fac[i] = 1LL * fac[i - 1] * i % P;
    }
    invfac[L] = power(fac[L], P - 2);
    for (int i = L; i; --i) {
        invfac[i - 1] = 1LL * invfac[i] * i % P;
    }
}

平面几何

点击展开
template<class T>
struct Point {
    T x, y;
    Point (const T &x_ = 0, const T &y_ = 0) : x(x_), y(y_) {}

    template<class U>
    operator Point<U>() {
        return Point<U>(U(x), U(y));
    }
    Point &operator+=(const Point &p) & {
        x += p.x, y += p.y;
        return *this;
    }
    Point &operator-=(const Point &p) & {
        x -= p.x, y -= p.y;
        return *this;
    }
    Point &operator*=(const T &v) & {
        x *= v, y *= v;
        return *this;
    }
    Point &operator/=(const T &v) & {
        x /= v, y /= v;
        return *this;
    }
    Point &operator-() const {
        return Point(-x, -y);
    }
    friend Point operator+(Point a, const Point &b) {
        return a += b;
    }
    friend Point operator-(Point a, const Point &b) {
        return a -= b;
    }
    friend Point operator*(Point a, const T &b) {
        return a *= b;
    }
    friend Point operator/(Point a, const T &b) {
        return a /= b;
    }
    friend Point operator*(const T &a, Point b) {
        return b *= a;
    }
    friend bool operator==(const Point &a, const Point &b) {
        return a.x == b.x && a.y == b.y;
    }
    friend std::istream &operator>>(std::istream &is, Point &p) {
        return is >> p.x >> p.y;
    }
    friend std::ostream &operator<<(std::ostream &os, const Point &p) {
        return os << "(" << p.x << ", " << p.y << ")";
    }
};

template<class T>
struct Line {
    Point<T> a;
    Point<T> b;
    Line(const Point<T> &a_ = Point<T>(), const Point<T> &b_ = Point<T>()) : a(a_), b(b_) {}
};

template<class T>
T dot(const Point<T> &a, const Point<T> &b) {
    return a.x * b.x + a.y * b.y;
}

template<class T>
T cross(const Point<T> &a, const Point<T> &b) {
    return a.x * b.y - a.y * b.x;
}

template<class T>
T square(const Point<T> &p) {
    return dot(p, p);
}

template<class T>
double length(const Point<T> &p) {
    return std::sqrt(square(p));
}

template<class T>
double length(const Line<T> &l) {
    return length(l.a - l.b);
}

template<class T>
Point<T> normalize(const Point<T> &p) {
    return p / length(p);
}

template<class T>
bool parallel(const Line<T> &l1, const Line<T> &l2) {
    return cross(l1.b - l1.a, l2.b - l2.a) == 0;
}

template<class T>
double distance(const Point<T> &a, const Point<T> &b) {
    return length(a - b);
}

template<class T>
double distancePL(const Point<T> &p, const Line<T> &l) {
    return std::abs(cross(l.a - l.b, l.a - p)) / length(l);
}

template<class T>
double distancePS(const Point<T> &p, const Line<T> &l) {
    if (dot(p - l.a, l.b - l.a) < 0) {
        return distance(p, l.a);
    }
    if (dot(p - l.b, l.a - l.b) < 0) {
        return distance(p, l.b);
    }
    return distancePL(p, l);
}

template<class T>
Point<T> rotate(const Point<T> &a) {
    return Point(-a.y, a.x);
}

template<class T>
int sgn(const Point<T> &a)  {
    return a.y > 0 || (a.y == 0 && a.x > 0) ? 1 : -1;
}

template<class T>
bool pointOnLineLeft(const Point<T> &p, const Line<T> &l) {
    return corss(l.b - l.a, p - l.a) > 0;
}

template<class T>
Point<T> lineIntersection(const Line<T> &l1, const Line<T> &l2) {
    return l1.a + (l1.b - l1.a) * (cross(l2.b - l2.a, l1.a - l2.a) / cross(l2.b - l2.a, l1.a - l1.b));
}

template<class T>
bool pointOnSegment(const Point<T> &p, const Line<T> &l) {
    return cross(p - l.a, l.b - l.a) == 0 
        && std::min(l.a.x, l.b.x) <= p.x && p.x <= std::max(l.a.x, l.b.x) 
        && std::min(l.a.y, l.b.y) <= p.y && p.y <= std::max(l.a.y, l.b.y); 
}

template<class T> 
bool pointInPolygon(const Point<T> &a, const std::vector<Point<T>> &p) {
    int n = size(p);
    for (int i = 0; i < n; ++i) {
        if (pointOnSegment(a, Line(p[i], p[(i + 1) % n]))) {
            return true;
        }
    }
    int t = 0;
    for (int i = 0; i < n; ++i) {
        auto u = p[i];
        auto v = p[(i + 1) % n];
        if (u.x < a.x && v.x >= a.x && pointOnLineLeft(a, Line(v, u))) {
            t ^= 1;
        }
        if (u.x >= a.x && v.x < a.x && pointOnLineLeft(a, Line(u, v))) {
            t ^= 1;
        }
    }
    return t == 1;
}

/**
 * 0: 不相交
 * 1: 严格相交
 * 2: 重合
 * 3: 在端点处相交
*/
template<class T>
std::tuple<int, Point<T>, Point<T>> segmentIntersection(const Line<T> &l1, const Line<T> &l2) {
    if (std::max(l1.a.x, l1.b.x) < std::min(l2.a.x, l2.b.x)
        || std::min(l1.a.x, l1.b.x) > std::max(l2.a.x, l2.b.x)
        || std::max(l1.a.y, l1.b.y) < std::min(l2.a.y, l2.b.y)
        || std::min(l1.a.y, l1.b.y) > std::max(l2.a.y, l2.b.y)) {
            return {0, Point<T>(), Point<T>()};
        }
    if (cross(l1.b - l1.a, l2.b - l2.a) == 0) {
        if (cross(l1.b - l1.a, l2.a - l1.a) != 0) {
            return {0, Point<T>(), Point<T>()};
        } else {
            auto maxx1 = std::max(l1.a.x, l1.b.x);
            auto minx1 = std::min(l1.a.x, l1.b.x);
            auto maxy1 = std::max(l1.a.y, l1.b.y);
            auto miny1 = std::min(l1.a.y, l1.b.y);
            auto maxx2 = std::max(l2.a.x, l2.b.x);
            auto minx2 = std::min(l2.a.x, l2.b.x);
            auto maxy2 = std::max(l2.a.y, l2.b.y);
            auto miny2 = std::min(l2.a.y, l2.b.y);
            Point<T> p1(std::max(minx1, minx2), std::max(miny1, miny2));
            Point<T> p2(std::min(maxx1, maxx2), std::min(maxy1, maxy2));
            if (!pointOnSegment(p1, l1)) {
                std::swap(p1.y, p2.y);
            }
            if (p1 == p2) {
                return {3, p1, p2};
            } else {
                return {2, p1, p2};
            }
        }
    }
    auto cp1 = cross(l2.a - l1.a, l2.b - l1.a);
    auto cp2 = cross(l2.a - l1.b, l2.b - l1.b);
    auto cp3 = cross(l1.a - l2.a, l1.b - l2.a);
    auto cp4 = cross(l1.a - l2.b, l1.b - l2.b);
    if ((cp1 > 0 && cp2 > 0) || (cp1 < 0 && cp2 < 0) || (cp3 > 0 && cp4 > 0) || (cp3 < 0 && cp4 < 0)) {
        return {0, Point<T>(), Point<T>()};
    }
    Point p = lineIntersection(l1, l2);
    if (cp1 != 0 && cp2 != 0 && cp3 != 0 && cp4 != 0) {
        return {1, p, p};
    } else {
        return {3, p, p};
    }
}

template<class T> 
double distanceSS(const Line<T> &l1, const Line<T> &l2) {
    if (std::get<0>(segmentIntersection(l1, l2)) != 0) {
        return 0.0;
    }
    return std::min({distancePS(l1.a, l2), distancePS(l1.b, l2), distancePS(l2.a, l1), distance(l2.b, l1)});
}

template<class T>
bool segmentInPolygon(const Line<T> &l, const std::vector<Point<T>> &p) {
    int n = size(p);
    if (!pointInPolygon(l.a, p) || !pointInPolygon(l.b, p)) {
        return false;
    }
    for (int i = 0; i < n; ++i) {
        auto u = p[i];
        auto v = p[(i + 1) % n];
        auto w = p[(i + 2) % n];
        auto [t, p1, p2] = segmentIntersection(l, Line(u, v));    
        if (t == 1) {
            return false;
        }
        if (t == 0) {
            continue;
        }
        if (t == 2) {
            if (pointOnSegment(v, l) && v != l.a && v != l.b) {
                if (cross(v - u, w - v) > 0) {
                    return false;
                }
            }
        } else {
            if (p1 != u && p1 != v) {
                if (pointOnLineLeft(l.a, Line(v, u))
                    || pointOnLineLeft(l.b, Line(v, u))) {
                    return false;
                }
            } else if (p1 == v) {
                if (l.a == v) {
                    if (pointOnLineLeft(u, l)) {
                        if (pointOnLineLeft(w, l)
                            && pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    } else {
                        if (pointOnLineLeft(w, l)
                            || pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    }
                } else if (l.b == v) {
                    if (pointOnLineLeft(u, Line(l.b, l.a))) {
                        if (pointOnLineLeft(w, Line(l.b, l.a))
                            && pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    } else {
                        if (pointOnLineLeft(w, Line(l.b, l.a))
                            || pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    }
                } else {
                    if (pointOnLineLeft(u, l)) {
                        if (pointOnLineLeft(w, Line(l.b, l.a))
                            || pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    } else {
                        if (pointOnLineLeft(w, l)
                            || pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    }
                }
            }
        }
    }
    return true;
}

template<class T>
std::vector<Point<T>> hp(std::vector<Line<T>> lines) {
    std::sort(lines.begin(), lines.end(), 
        [&](auto l1, auto l2) {
            auto d1 = l1.b - l1.a;
            auto d2 = l2.b - l2.a;
            if (sgn(d1) != sgn(d2)) {
                return sgn(d1) == 1;
            }
            return cross(d1, d2) > 0;
        });
    
    std::deque<Line<T>> ls;
    std::deque<Point<T>> ps;
    for (auto l : lines) {
        if (ls.empty()) {
            ls.push_back(l);
            continue;
        }
        while (!ps.empty() && !pointOnLineLeft(ps.back(), l)) {
            ps.pop_back();
            ls.pop_back();
        }
        while (!ps.empty() && !pointOnLineLeft(ps[0], l)) {
            ps.pop_front();
            ls.pop_front();
        }
        if (cross(l.b - l.a, ls.back().b - ls.back().a) == 0) {
            if (dot(l.b - l.a, ls.back().b - ls.back().a) > 0) {        
                if (!pointOnLineLeft(ls.back().a, l)) {
                    assert(ls.size() == 1);
                    ls[0] = l;
                }
                continue;
            }
            return {};
        }
        ps.push_back(lineIntersection(ls.back(), l));
        ls.push_back(l);
    }    
    while (!ps.empty() && !pointOnLineLeft(ps.back(), ls[0])) {
        ps.pop_back();
        ls.pop_back();
    }
    if (ls.size() <= 2) {
        return {};
    }
    ps.push_back(lineIntersection(ls[0], ls.back()));
    return std::vector(ps.begin(), ps.end());
}

template<class T>
auto getHull(std::vector<Point<T>> p) {
    std::sort(p.begin(), p.end(), 
        [&](auto a, auto b) {
            return a.x < b.x || (a.x == b.x && a.y < b.y);
        });
    std::vector<Point<T>> hi, lo;
    for (auto p : p) {
        while (size(hi) > 1 && cross(hi.back() - hi.rbegin()[1], p - hi.back()) >= 0) {
            hi.pop_back();
        }
        while (size(hi) && hi.back().x == p.x) {
            hi.pop_back();
        }
        hi.push_back(p);
        while (size(lo) > 1 && cross(lo.back() - lo.rbegin()[1], p - hi.back()) >= 0) {
            lo.pop_back();
        }
        if (size(lo) == 0 || lo.back().x < p.x) {
            lo.push_back(p);
        }
    }
    return std::make_pair(hi, lo);
}

using real = long double;
using P = Point<real>;

constexpr real eps = 0;
posted @ 2024-10-23 23:44  sleeeeeping  阅读(45)  评论(0编辑  收藏  举报