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 @   sleeeeeping  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
  1. 1 吹梦到西洲 恋恋故人难,黄诗扶,妖扬
  2. 2 敢归云间宿 三无Marblue
敢归云间宿 - 三无Marblue
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

敢归云间宿 - 三无Marblue

词:怀袖

曲:KBShinya

编曲:向往

策划:杨颜

监制:似雨若离RainJaded/杨颜

吉他:大牛

混音:三无Marblue

和声:雾敛

母带:张锦亮

映像:似雨若离RainJaded

美术:阿尼鸭Any-a/乙配/雨谷/今风/米可/Eluan

题字:长安

酒 泼去群山眉头

酒 泼去群山眉头

月 悬在人世沧流

空杯如行舟 浪荡醉梦里走

心 生自混沌尽头

心 生自混沌尽头

对 天地自斟自酬

诗随我 遍历春秋

行流水 走笔形生意动

见珠玉 淙淙落纸成诵

拾得浮名有几声 云深处 却空空

耳畔丝竹 清商如雾

谈笑间 却是心兵穷途

飞觞醉月无归宿 便是孤独

不如就化身为风

卷狂沙 侵天幕

吹醒那 泉下望乡 的战骨

昨日边关犹灯火

眼前血海翻覆

千万人跌落青史 隔世号呼

于是沸血重剑共赴

斩以雷霆之怒

肩背相抵破阵开路

万古同歌哭

纵他春风不度 悲欢蚀骨

此去宁作吾

挣过命途 才敢写荣枯

望 云际群龙回首

望 云际群龙回首

任 飘蓬争逐身后

叹冥顽之俦 好景尽付恩仇

收 江声随酒入喉

收 江声随酒入喉

来 提笔御风同游

不觉已 换了春秋

真亦假 泼墨腾烟沉陆

有还无 蝶影纷堕幻目

我与天地周旋久

写尽梦 便成梦

夜雨浇熄 往事残烛

生死间 谁尽兴谁辜负

管他醒来归何处 心生万物

也曾对电光火雨

抛酒樽 镇天枢

护住了 人间多少 朝与暮

烧尽了阴云冥府

烧尽了阴云冥府

且看星斗尽出

浩荡荡尘埃野马 忘怀命数

于是信步鸿蒙之轻

也领苍生之重

与诗与剑颠倒与共

沉眠斜阳中

纵他世事汹涌 万类争渡

此去宁作吾

醉得糊涂 才梦得清楚

潮水 带着叹息轻抚

潮水 带着叹息轻抚

像光阴 漫过大地上幽微草木

有情世 见众生明灭往复

天生自在 何必回顾

晦暗中双掌一拊

立此身 照前路

与某个 阔别的我 决胜负

渺渺兮身外无物

无喜无悲无怖

不过是大梦一场 各自沉浮

于是纵横万相穷通

也守心底灵通

合眼识得星沉地动

也岿然不动

敢令岁月乌有 逍遥长驻

敢归云间宿

遥祝远行人 有道不孤

点击右上角即可分享
微信分享提示