我的算法竞赛模板

本模板已开源于GitHub,仓库地址,欢迎点星星和给我填issue

板子

目录


让代码跑起来

编译运行

g++ -std=c++23 a.cpp -o a.out
./a.out

对拍器

Linux Shell

# 请根据实际的C++版本修改-std版本
g++ generator.cpp -o gen.out -std=c++20 -O2 || exit 1
g++ solution.cpp -o sol.out -std=c++20 -O2 || exit 1
g++ brute_force.cpp -o bf.out -std=c++20 -O2 || exit 1
mkdir -p tc
cnt=1
while true; do
	echo "Running test $cnt"
	./gen.out > tc/input.txt
	timeout 2s ./sol.out < tc/input.txt > tc/sol.txt
	exit_code=$?
	if [ $exit_code -ne 0 ]; then
		echo -e "\033[31mRuntime error or time limit exceeded on solution\033[0m"
		break
	fi
	timeout 2s ./bf.out < tc/input.txt > tc/bf.txt
	if [ $exit_code -ne 0 ]; then
		echo -e "\033[31mRuntime error or time limit exceeded on brute force\033[0m"
		break
	fi
	if ! diff -wB tc/sol.txt tc/bf.txt > /dev/null; then
		echo -e "\033[31mWrong Answer on test case $cnt\033[0m"
		echo "Input:"
		cat tc/input.txt
		echo -e "\nSolution output:"
		cat tc/sol.txt
		echo -e "\nBrute force output:"
		cat tc/bf.txt
		break
	fi
	echo -e "\033[32mAccepted\033[0m"
	((cnt++))
done

Windows Powershell

function check1($msg) {
    if($LASTEXITCODE -ne 0) {
        Write-Host "Compile error: $msg" -ForegroundColor Red
        exit 1
    }
}
function check2($msg) {
    if($LASTEXITCODE -ne 0) {
        Write-Host "Runtime error: $msg" -ForegroundColor Red
        exit 2
    }
}
g++ generator.cpp -o gen.exe -std=c++20 -O2
check1 "generator"
g++ solution.cpp -o sol.exe -std=c++20 -O2 -Wall
check1 "solution"
g++ brute_force.cpp -o bf.exe -std=c++20 -O2 -Wall
check1 "brute force"
mkdir -p testcases
$cnt = 1
while($cnt -le 100000) {
    Write-Host "Running test case $cnt"
    gen.exe > "testcases/input.txt"
    check2 "generator"
    Get-Content "testcases/input.txt" | sol.exe > "testcases/sol.out"
    check2 "solution"
    Get-Content "testcases/input.txt" | bf.exe > "testcases/bf.out"
    check2 "brute_force"
    $f1 = (Get-Content "testcases/sol.out" -Raw) -replace '\s+', ''
    $f2 = (Get-Content "testcases/bf.out" -Raw) -replace '\s+', ''
    if($f1 -ne $f2) {
        Write-Host "Wrong answer on $cnt" -ForegroundColor Red
        Write-Host "Input:"
        Get-Content "testcases/input.txt"
        Write-Host "Solution output:"
        Get-Content "testcases/sol.out"
        Write-Host "Brute force output:"
        Get-Content "testcases/bf.out"
        exit 3
    }
    Write-Host "Accepted" -ForegroundColor Green
    $cnt++
}

Debug宏

#ifdef ONLINE_JUDGE
#define debug(...) (void(0))
#else

string trim(string s) {
	int l = 0, r = s.size() - 1;
	while(l <= r && isspace(s[l])) ++l;
	while(r >= l && isspace(s[r])) --r;
	return s.substr(l, r - l + 1);
}

vector<string> split_args(const string &s) {
	vector<string> ret;
	string cur;
	int dep = 0;
	for(char ch : s) {
		if(ch == '(' || ch == '[') {
			++dep;
			cur += ch;
		} else if(ch == ')' || ch == ']') {
			--dep;
			cur += ch;
		} else if(dep == 0 && ch == ',') {
			ret.push_back(trim(cur));
			cur = "";
		} else {
			cur += ch;
		}
	}
	if(!(trim(cur).empty())) ret.push_back(trim(cur));
	return ret;
}

template<class Tuple, size_t ... I> void print_tuple
	(const vector<string> &names, const Tuple &t, index_sequence<I...>) {
	using expr = int[];
	bool first = true;
	(void)expr{0, ((void)(
		(first ? first = false : (bool)(cerr << ", ")),
		cerr << names[I] << " = " << get<I>(t)
	), 0)...};
}

template<class ... Args> void debug_helper(const string &s, Args &&... args) {
	auto names = split_args(s);
	auto tp = forward_as_tuple(forward<Args>(args)...);
	print_tuple(names, tp, make_index_sequence<sizeof...(Args)>{});
	cerr << endl;
}

#define debug(...) debug_helper(#__VA_ARGS__, __VA_ARGS__)
#endif

数据结构

线段树

template<class Info> concept SegInfo = requires(Info a, Info b) {
	Info{};
	{ a + b } -> same_as<Info>;
	{ a.update(b) } -> same_as<void>;
};

template<SegInfo Info> struct SegTree {
	SegTree() : n(0) {}
	explicit SegTree(int sz) : n(sz), info(sz * 4, Info()) {}
	explicit SegTree(const vector<Info> &v) : n(v.size()), info(v.size() * 4) {
		_build(v, 0, 0, n);
	}
	void assign(int sz) {
		n = sz;
		info.assign(n * 4, Info());
	}
	void assign(const vector<Info> &v) {
		n = v.size();
		info.assign(n * 4, Info());
		_build(v, 0, 0, n);
	}
	Info query(int l, int r) const {
		if(l == r) return Info();
		return _query(l, r, 0, 0, n);
	}
	void update(int x, const Info &v) {
		_update(x, v, 0, 0, n);
	}
private:
	int n;
	vector<Info> info;
	void _build(const vector<Info> &v, int u, int rl, int rr) {
		if(rl == rr - 1) {
			info[u] = v[rl];
			return;
		}
		int mid = (rl + rr) / 2, ls = u * 2 + 1, rs = u * 2 + 2;
		_build(v, ls, rl, mid);
		_build(v, rs, mid, rr);
		info[u] = info[ls] + info[rs];
	}
	Info _query(int ql, int qr, int u, int rl, int rr) const {
		if(ql <= rl && qr >= rr) return info[u];
		int mid = (rl + rr) >> 1, ls = u * 2 + 1, rs = u * 2 + 2;
		Info res{};
		if(ql < mid) res = res + _query(ql, qr, ls, rl, mid);
		if(qr > mid) res = res + _query(ql, qr, rs, mid, rr);
		return res;
	}
	void _update(int x, const Info &v, int u, int rl, int rr) {
		if(rl == rr - 1) {
			v.update(info[u]);
			return;
		}
		int mid = (rl + rr) >> 1, ls = u * 2 + 1, rs = u * 2 + 2;
		if(x < mid) _update(x, v, ls, rl, mid);
		else _update(x, v, rs, mid, rr);
		info[u] = info[ls] + info[rs];
	}
};

struct Info {
	Info() {
		
	}
	Info operator+(const Info &i) const {
		
	}
	void update(Info &dst) const {
		
	}
};

template<class Info, class Tag> concept SegInfoTag
	= requires(Info a, Info b, Tag c, Tag d, int l, int r) {
	Info{};
	Tag{};
	{ a + b } -> same_as<Info>;
	{ c.apply(a, l, r) } -> same_as<void>;
	{ c.apply(d, l, r) } -> same_as<void>;
	{ c.empty() } -> same_as<bool>;
	{ c.clear() } -> same_as<void>;
} && !is_same_v<Info, bool>;
template<class Info, class Tag> requires(SegInfoTag<Info, Tag>) struct LazySegTree {
	LazySegTree() : n(0) {}
	explicit LazySegTree(int n_) : n(n_), info(4 * n_), tag(4 * n_) {}
	explicit LazySegTree(const vector<Info> &v) : n(v.size())
		, info(4 * v.size()), tag(4 * v.size()) {
		_build(v, 0, 0, n);
	}
	void assign(int n_) {
		n = n_;
		info.assign(4 * n_, Info{});
		tag.assign(4 * n_, Tag{});
	}
	void assign(const vector<Info> &v) {
		n = v.size();
		info.assign(4 * v.size(), Info{});
		tag.assign(4 * v.size(), Tag{});
		_build(v, 0, 0, n);
	}
	Info query(int l, int r) {
		return _query(l, r, 0, 0, n);
	}
	void update(int l, int r, const Tag &t) {
		_update(l, r, t, 0, 0, n);
	}
private:
	int n;
	vector<Info> info;
	vector<Tag> tag;
	void _build(const vector<Info> &v, int u, int rl, int rr) {
		if(rr - rl == 1) {
			info[u] = v[rl];
			return;
		}
		int mid = (rl + rr) / 2, ls = u * 2 + 1, rs = u * 2 + 2;
		_build(v, ls, rl, mid); _build(v, rs, mid, rr);
		info[u] = info[ls] + info[rs];
	}
	void _pushdown(int u, int rl, int rr) {
		if(tag[u].empty()) return;
		int mid = (rl + rr) / 2, ls = u * 2 + 1, rs = u * 2 + 2;
		tag[u].apply(info[ls], rl, mid);
		tag[u].apply(info[rs], mid, rr);
		tag[u].apply(tag[ls], rl, mid);
		tag[u].apply(tag[rs], mid, rr);
		tag[u].clear();
	}
	Info _query(int ql, int qr, int u, int rl, int rr) {
		if(ql <= rl && qr >= rr) return info[u];
		_pushdown(u, rl, rr);
		Info ret{};
		int mid = (rl + rr) / 2, ls = u * 2 + 1, rs = u * 2 + 2;
		if(ql < mid) ret = ret + _query(ql, qr, ls, rl, mid);
		if(qr > mid) ret = ret + _query(ql, qr, rs, mid, rr);
		return ret;
	}
	void _update(int ul, int ur, const Tag &t, int u, int rl, int rr) {
		if(ul <= rl && ur >= rr) {
			t.apply(info[u], rl, rr);
			t.apply(tag[u], rl, rr);
			return;
		}
		_pushdown(u, rl, rr);
		int mid = (rl + rr) / 2, ls = u * 2 + 1, rs = u * 2 + 2;
		if(ul < mid) _update(ul, ur, t, ls, rl, mid);
		if(ur > mid) _update(ul, ur, t, rs, mid, rr);
		info[u] = info[ls] + info[rs];
	}
};

struct Info {
	Info() {
		
	}
	Info operator+(const Info &i) const {
		
	}
};
struct Tag {
	Tag() {
		
	}
	bool empty() const {
		
	}
	void clear() {
		
	}
	void apply(Info &dst, int l, int r) const {
		
	}
	void apply(Tag &dst, int l, int r) const {
		
	}
};

动态开点线段树

template<class Info> concept DynSegInfo = requires(Info a, Info b, ll l, ll r) {
	Info{};
	Info(l, r);
	{ a + b } -> same_as<Info>;
	{ a.apply(b) } -> same_as<void>;
	{ a.l } -> convertible_to<ll>;
	{ a.r } -> convertible_to<ll>;
	{ a.ls } -> convertible_to<ll>;
	{ a.rs } -> convertible_to<ll>;
};

template<DynSegInfo Info> struct DynSegTree {
	explicit DynSegTree(ll _n) : n(_n), info(1, Info(0, _n)) {}
	void update(ll x, const Info &src) {
		_update(x, src, 0);
	}
	Info query(ll l, ll r) const {
		return _query(l, r, 0);
	}
private:
	vector<Info> info;
	ll n;
	void _update(ll x, const Info &src, ll u) {
		if(info[u].l == info[u].r - 1) {
			src.apply(info[u]);
			return;
		}
		ll mid = (info[u].l + info[u].r) / 2;
		if(x < mid) {
			if(info[u].ls == -1) {
				info[u].ls = info.size();
				info.emplace_back(info[u].l, mid);
			}
			_update(x, src, info[u].ls);
		} else {
			if(info[u].rs == -1) {
				info[u].rs = info.size();
				info.emplace_back(mid, info[u].r);
			}
			_update(x, src, info[u].rs);
		}
		ll _l = info[u].l, _r = info[u].r, _ls = info[u].ls, _rs = info[u].rs;
		info[u] = Info{};
		if(_ls != -1) info[u] = info[u] + info[_ls];
		if(_rs != -1) info[u] = info[u] + info[_rs];
		info[u].l = _l; info[u].r = _r; info[u].ls = _ls; info[u].rs = _rs;
	}
	Info _query(ll ql, ll qr, ll u) const {
		if(ql <= info[u].l && qr >= info[u].r) return info[u];
		Info ret{};
		ll mid = (info[u].l + info[u].r) / 2;
		if(ql < mid && info[u].ls != -1) ret = ret + _query(ql, qr, info[u].ls);
		if(qr > mid && info[u].rs != -1) ret = ret + _query(ql, qr, info[u].rs);
		return ret;
	}
};

struct Info {
	ll l, r, ls, rs;
	Info() : l(0), r(0), ls(-1), rs(-1) {
		
	}
	Info(ll _l, ll _r) : l(_l), r(_r), ls(-1), rs(-1) {
		
	}
	Info operator+(const Info &i) const {
		Info ret{};

		return ret;
	}
	void apply(Info &dst) const {

	}
};

权值线段树

// value segment tree
struct VST {
	explicit VST(int maxn) : sum((maxn + 1) * 4), n(maxn + 1) {}
	VST() : n(0) {}
	void assign(int maxn) {
		sum.assign((maxn + 1) * 4, 0);
		n = maxn + 1;
	}
	void insert(int x) {
		_update(x, 1, 0, 0, n);
	}
	void erase(int x) {
		_update(x, -1, 0, 0, n);
	}
	int qpre(int x) const {
		int rk = qrv(x);
		return qvr(rk - 1);
	}
	int qsuc(int x) const {
		int rk = qrv(x + 1);
		return qvr(rk);
	}
	int qrv(int x) const {
		return _query(0, x, 0, 0, n);
	}
	int qvr(int k) const {
		return _qvr(k, 0, 0, n);
	}
	int size() const {
		return sum[0];
	}
	int qcnt(int x) const {
		return _query(x, x + 1, 0, 0, n);
	}
	int qrange_cnt(int l, int r) const {
		return _query(l, r, 0, 0, n);
	}
private:
	vector<int> sum;
	int n;
	void _update(int x, int dv, int rt, int rl, int rr) {
		if(rr - rl == 1) {
			sum[rt] += dv;
			return;
		}
		int mid = (rl + rr) >> 1, lson = rt * 2 + 1, rson = rt * 2 + 2;
		if(x < mid) {
			_update(x, dv, lson, rl, mid);
		} else {
			_update(x, dv, rson, mid, rr);
		}
		sum[rt] = sum[lson] + sum[rson];
	}
	ll _query(int ql, int qr, int rt, int rl, int rr) const {
		if(ql <= rl && qr >= rr)
			return sum[rt];
		int mid = (rl + rr) >> 1, lson = rt * 2 + 1, rson = rt * 2 + 2;
		ll ans = 0;
		if(ql < mid) {
			ans += _query(ql, qr, lson, rl, mid);
		}
		if(qr > mid) {
			ans += _query(ql, qr, rson, mid, rr);
		}
		return ans;
	}
	int _qvr(int k, int rt, int rl, int rr) const {
		if(rr - rl == 1)
			return rl;
		int mid = (rl + rr) >> 1, lson = rt * 2 + 1, rson = rt * 2 + 2;
		if(k < sum[lson]) {
			return _qvr(k, lson, rl, mid);
		} else {
			return _qvr(k - sum[lson], rson, mid, rr);
		}
	}
};

主席树

// persistent segment tree
struct PST {
	const int n;
	explicit PST(int n_) : n(n_) {
		nodes.reserve(2 * n * (int)log2(n));
		vers.reserve(n + 1);
		vers.push_back(_build(0, n - 1));
	}
	void update(int k) {
		vers.push_back(_update(k, vers.back(), 0, n - 1));
	}
	// 这里为解决静态区间第k小问题设计,传入k, l, r
	int query(int k, int ver1, int ver2) const {
		return _query(k, vers[ver1 - 1], vers[ver2], 0, n - 1);
	}
private:
	struct Node {
		int l, r;
		int cnt;
	};
	vector<Node> nodes;
	vector<int> vers;
	int _build(int l, int r) {
		int ret = nodes.size();
		if(l == r) {
			nodes.emplace_back(-1, -1, 0);
			return ret;
		}
		nodes.emplace_back(-1, -1, 0);
		int mid = (l + r) >> 1;
		nodes[ret].l = _build(l, mid);
		nodes[ret].r = _build(mid + 1, r);
		return ret;
	}
	int _update(int k, int root, int l, int r) {
		int ret = nodes.size();
		nodes.emplace_back(nodes[root]);
		nodes[ret].cnt++;
		if(l == r) {
			return ret;
		}
		int mid = (l + r) >> 1;
		if(k <= mid) {
			nodes[ret].l = _update(k, nodes[root].l, l, mid);
		} else {
			nodes[ret].r = _update(k, nodes[root].r, mid + 1, r);
		}
		return ret;
	}
	int _query(int k, int root1, int root2, int l, int r) const {
		if(l == r) return l;
		int mid = (l + r) >> 1;
		int cntl = nodes[nodes[root2].l].cnt - nodes[nodes[root1].l].cnt;
		if(k <= cntl) {
			return _query(k, nodes[root1].l, nodes[root2].l, l, mid);
		} else {
			return _query(k - cntl, nodes[root1].r, nodes[root2].r, mid + 1, r);
		}
	}
};

树状数组(单点、区间)

// 单点
struct Fenwick {
	explicit Fenwick(int n) : n(n), nums(n + 1, 0) {}
	ll query(int x) const {
		ll ans = 0;
		for(; x; x -= lbit(x)) {
			ans += nums[x];
		}
		return ans;
	}
	void update(int x, ll v) {
		for(; x <= n; x += lbit(x)) {
			nums[x] += v;
		}
	}
private:
	vector<ll> nums;
	int n;
	static int lbit(int x) {
		return x & -x;
	}
};
// 区间
struct RangeFenwick {
	const int n;
	RangeFenwick(int n)
		: n(n), f1(n), f2(n) {}
	void update(int l, int r, ll v) {
		_update(l, v);
		_update(r + 1, -v);
	}
	ll query(int l, int r) const {
		return _query(r) - _query(l - 1);
	}
private:
	Fenwick f1, f2;
	void _update(int x, ll v) {
		f1.update(x, v);
		f2.update(x, v * (x - 1));
	}
	ll _query(int x) const {
		return f1.query(x) * x - f2.query(x);
	}
};

差分数组

使用差分数组区间加等差数列

// 二次差分
vector<ll> diff(n + 3, 0);
// [1,l]第i项加i
diff[1] += val;
diff[l + 1] -= val;
// [r+1,l+r]第i项减i-r
diff[r + 1] -= val;
diff[l + r + 1] += val;
// 还原
for(int _ = 0; _ < 2; ++_) {
	for(int i = 1; i < n + 3; ++i) {
		diff[i] += diff[i - 1];
	}
}

树状数组在线处理

struct ArithmeticFenwick {
	explicit ArithmeticFenwick(int n)
		: f1(n + 2), f2(n + 2) {}
	// a*idx+b
	void update(int l, int r, ll a, ll b) {
		f1.update(l, b);
		f1.update(r + 1, -b - a * (r - l + 1));
		f2.update(l, a);
		f2.update(r + 1, -a);
	}
	ll query(ll idx) const {
		return f1.query(idx) + idx * f2.query(idx);
	}
private:
	Fenwick f1, f2;
};

分块

struct Block {
	Block() : l(-1), r(-1) {}
	// 注意左闭右开
	Block(int l, int r, const vector<ll> &nums_)
		: nums(r - l), lazy(0), sum(0), l(l), r(r) {
		for(int i = l; i < r; ++i) {
			nums[i - l] = nums_[i];
			sum += nums[i - l];
		}
	}
	// 注意左闭右开
	void update(int ul, int ur, ll dval) {
		if(ul > r || ur < l) {
			return;
		}
		if(ul <= l && ur >= r) {
			lazy += dval;
			sum += dval * (r - l);
			return;
		}
		for(int i = max(ul, l); i < min(ur, r); ++i) {
			nums[i - l] += dval;
		}
		sum += dval * (min(ur, r) - max(ul, l));
	}
	// 注意左闭右开
	ll query(int ql, int qr) const {
		if(ql >= r || qr <= l) {
			return 0;
		}
		if(ql <= l && qr >= r) {
			return sum;
		}
		ll ans = lazy * (min(qr, r) - max(ql, l));
		for(int i = max(ql, l); i < min(qr, r); ++i) {
			ans += nums[i - l];
		}
		return ans;
	}
private:
	vector<ll> nums;
	ll lazy;
	ll sum;
	int l, r;
};
struct BlockedArray {
	explicit BlockedArray(const vector<ll> &nums) {
		int r = nums.size();
		int len = sqrt(r + 0.5);
		int size = (r + len - 1) / len;
		blocks.reserve(size);
		for(int i = 0; i < size; ++i) {
			int ll = i * len, rr = min((i + 1) * len, r);
			blocks.emplace_back(ll, rr, nums);
		}
	}
	void update(int l, int r, ll d) {
		for(auto &blk : blocks) {
			blk.update(l, r, d);
		}
	}
	ll query(int l, int r) const {
		ll ans = 0;
		for(const auto &blk : blocks) {
			ans += blk.query(l, r);
		}
		return ans;
	}
	vector<Block> blocks;
};

并查集

// 按秩合并
struct DSU {
	explicit DSU(int n) : fa(n), rk(n, 1) {
		iota(fa.begin(), fa.end(), 0);
	}
	int find(int x) {
		if(fa[x] != x) {
			fa[x] = find(fa[x]);
		}
		return fa[x];
	}
	bool is_connected(int x, int y) {
		return find(x) == find(y);
	}
	void connect(int x, int y) {
		x = find(x);
		y = find(y);
		if(x == y) return;
		if(rk[x] < rk[y]) {
			swap(x, y);
		}
		if(rk[x] == rk[y]) {
			rk[x]++;
		}
		fa[y] = x;
	}
private:
	vector<int> fa, rk;
};
// 随机合并
struct DSU_Random {
	explicit DSU_Random(int n) : fa(n) {
		iota(fa.begin(), fa.end(), 0);
	}
	int find(int x) {
		if(fa[x] != x) {
			fa[x] = find(fa[x]);
		}
		return fa[x];
	}
	bool is_connected(int x, int y) {
		return find(x) == find(y);
	}
	void connect(int x, int y) {
		int fx = find(x), fy = find(y);
		if(rnd() % 2) {
			fa[fx] = fy;
		} else {
			fa[fy] = fx;
		}
	}
private:
	static inline mt19937 rnd{ (unsigned int)chrono::steady_clock::now()
		.time_since_epoch().count() };
	vector<int> fa;
};

无旋Treap

constexpr int inf = 0x3f3f3f3f;
struct Treap {
	Treap() : rt(nullptr) {}
	~Treap() {
		if(rt) {
			_dtor(rt);
		}
	}
	void insert(int v) {
		auto tmp = _sval(rt, v);
		auto l = _sval(tmp.first, v - 1);
		Node *node = l.second;
		if(!l.second) {
			node = new Node(v);
		} else {
			l.second->cnt++;
			l.second->usize();
		}
		Node *lc = _merge(l.first, node);
		rt = _merge(lc, tmp.second);
	}
	void erase(int v) {
		auto tmp = _sval(rt, v);
		auto l = _sval(tmp.first, v - 1);
		if(l.second->cnt > 1) {
			l.second->cnt--;
			l.second->usize();
			l.first = _merge(l.first, l.second);
		} else {
			if(tmp.first == l.second) {
				tmp.first = nullptr;
			}
			delete l.second;
			l.second = nullptr;
		}
		rt = _merge(l.first, tmp.second);
	}
	// 注意0-based
	int qrv(int v) const {
		return _qrv(rt, v);
	}
	// 注意0-based
	int qvr(int r) const {
		return _qvr(rt, r);
	}
	// 注意是小于的,不是大于等于的
	int lower_bound(int v) const {
		auto tmp = _sval(rt, v - 1);
		int ret = -inf;
		if(tmp.first) {
			ret = _qvr(tmp.first, tmp.first->size - 1);
		}
		rt = _merge(tmp.first, tmp.second);
		return ret;
	}
	int upper_bound(int v) const {
		auto tmp = _sval(rt, v);
		int ret = -inf;
		if(tmp.second) {
			ret = _qvr(tmp.second, 0);
		}
		rt = _merge(tmp.first, tmp.second);
		return ret;
	}
private:
	struct Node {
		int val;
		int cnt;
		int size;
		int prio;
		Node *left, *right;
		static inline random_device rnd{};
		Node(int v) : val(v), cnt(1), size(1)
			, prio(rnd()), left(nullptr), right(nullptr) {}
		void usize() {
			size = cnt;
			if(left) {
				size += left->size;
			}
			if(right) {
				size += right->size;
			}
		}
	};
	mutable Node *rt;
	static void _dtor(Node *ptr) {
		if(ptr->left) {
			_dtor(ptr->left);
			ptr->left = nullptr;
		}
		if(ptr->right) {
			_dtor(ptr->right);
			ptr->right = nullptr;
		}
		delete ptr;
	}
	static pair<Node *, Node *> _sval(Node *const ptr, int key) {
		if(!ptr) {
			return { nullptr, nullptr };
		}
		if(ptr->val <= key) {
			auto tmp = _sval(ptr->right, key);
			ptr->right = tmp.first;
			ptr->usize();
			return { ptr, tmp.second };
		} else {
			auto tmp = _sval(ptr->left, key);
			ptr->left = tmp.second;
			ptr->usize();
			return { tmp.first, ptr };
		}
	}
	static tuple<Node *, Node *, Node *> _srnk(Node *const ptr, int rnk) {
		if(!ptr) return { nullptr, nullptr, nullptr };
		int lsize = (ptr->left ? ptr->left->size : 0);
		if(rnk < lsize) {
			auto [lptr, mptr, rptr] = _srnk(ptr->left, rnk);
			ptr->left = rptr;
			ptr->usize();
			return { lptr, mptr, ptr };
		} else if(rnk < lsize + ptr->cnt) {
			Node *lptr = ptr->left;
			Node *rptr = ptr->right;
			ptr->left = ptr->right = nullptr;
			return { lptr, ptr, rptr };
		} else {
			auto [lptr, mptr, rptr] = _srnk(ptr->right, rnk - lsize - ptr->cnt);
			ptr->right = lptr;
			ptr->usize();
			return { ptr, mptr, rptr };
		}
	}
	static Node *_merge(Node *const u, Node *const v) {
		if(!u) return v;
		if(!v) return u;
		if(u->prio < v->prio) {
			u->right = _merge(u->right, v);
			u->usize();
			return u;
		} else {
			v->left = _merge(u, v->left);
			v->usize();
			return v;
		}
	}
	int _qrv(Node *const ptr, int val) const {
		auto [l, r] = _sval(ptr, val - 1);
		int ret = (l ? l->size : 0);
		rt = _merge(l, r);
		return ret;
	}
	int _qvr(Node *const ptr, int r) const {
		auto [lptr, mptr, rptr] = _srnk(ptr, r);
		int ret = mptr->val;
		rt = _merge(_merge(lptr, mptr), rptr);
		return ret;
	}
};

无旋Treap带修

struct Treap {
	Treap() : rt(nullptr) {}
	~Treap() {
		if(rt) {
			_dtor(rt);
		}
	}
	void insert(int v) {
		auto tmp = _sval(rt, v);
		auto l = _sval(tmp.first, v - 1);
		Node *node = l.second;
		if(!l.second) {
			node = new Node(v);
		} else {
			l.second->cnt++;
			l.second->usize();
		}
		Node *lc = _merge(l.first, node);
		rt = _merge(lc, tmp.second);
	}
	void reverse(int l, int r) {
		auto [a, b] = _srev(rt, l);
		auto [c, d] = _srev(b, r - l);
		if(c) c->rev ^= true;
		rt = _mrev(a, _mrev(c, d));
	}
	vector<int> inorder() const {
		vector<int> ret;
		ret.reserve(rt->size);
		auto dfs = [&](auto &&dfs, Node *ptr) -> void {
			if(!ptr) return;
			ptr->urev();
			dfs(dfs, ptr->left);
			ret.push_back(ptr->val);
			dfs(dfs, ptr->right);
		};
		dfs(dfs, rt);
		return ret;
	}
private:
	struct Node {
		int val, cnt, size, prio;
		Node *left, *right;
		bool rev;
		static inline random_device rnd{};
		Node(int v) : val(v), cnt(1), size(1)
			, prio(rnd()), left(nullptr), right(nullptr), rev(false) {}
		void usize() {
			size = cnt;
			if(left) size += left->size;
			if(right) size += right->size;
		}
		void urev() {
			if(rev) {
				swap(left, right);
				if(left != nullptr) left->rev ^= true;
				if(right != nullptr) right->rev ^= true;
				rev = false;
			}
		}
	};
	mutable Node *rt;
	static void _dtor(Node *ptr) {
		if(ptr->left) {
			_dtor(ptr->left);
			ptr->left = nullptr;
		}
		if(ptr->right) {
			_dtor(ptr->right);
			ptr->right = nullptr;
		}
		delete ptr;
	}
	static pair<Node *, Node *> _sval(Node *const ptr, int key) {
		if(!ptr) {
			return { nullptr, nullptr };
		}
		if(ptr->val <= key) {
			auto tmp = _sval(ptr->right, key);
			ptr->right = tmp.first;
			ptr->usize();
			return { ptr, tmp.second };
		} else {
			auto tmp = _sval(ptr->left, key);
			ptr->left = tmp.second;
			ptr->usize();
			return { tmp.first, ptr };
		}
	}
	static Node *_merge(Node *const u, Node *const v) {
		if(!u) return v;
		if(!v) return u;
		if(u->prio < v->prio) {
			u->right = _merge(u->right, v);
			u->usize();
			return u;
		} else {
			v->left = _merge(u, v->left);
			v->usize();
			return v;
		}
	}
	static pair<Node *, Node *> _srev(Node *const ptr, int k) {
		if(!ptr) return {nullptr, nullptr};
		ptr->urev();
		if(k == 0 || (ptr->left && ptr->left->size >= k)) {
			auto [pf, ps] = _srev(ptr->left, k);
			ptr->left = ps;
			ptr->usize();
			return {pf, ptr};
		} else {
			auto [pf, ps] = _srev(ptr->right,
				k - 1 - (ptr->left ? ptr->left->size : 0));
			ptr->right = pf;
			ptr->usize();
			return {ptr, ps};
		}
	}
	static Node *_mrev(Node *const a, Node *const b) {
		if(!a) return b;
		if(!b) return a;
		if(a->prio < b->prio) {
			a->urev();
			a->right = _mrev(a->right, b);
			a->usize();
			return a;
		} else {
			b->urev();
			b->left = _mrev(a, b->left);
			b->usize();
			return b;
		}
	}
};

树的倍增算法

struct BinaryLift {
	explicit BinaryLift(int n) : tree(n), anc(n, vector<int>(LOG, -1)), depth(n) {}
	void addedge(int u, int v) {
		tree[u].push_back(v);
		tree[v].push_back(u);
	}
	void build(int root) {
		dfs(root, -1);
	}
	int lca(int u, int v) const {
		if(depth[u] < depth[v]) swap(u, v);
		for(int k = LOG - 1; k >= 0; --k) {
			if(anc[u][k] != -1) {
				if(depth[anc[u][k]] >= depth[v]) {
					u = anc[u][k];
				}
			}
		}
		if(u == v) return u;
		for(int k = LOG - 1; k >= 0; --k) {
			if(anc[u][k] != anc[v][k]) {
				u = anc[u][k];
				v = anc[v][k];
			}
		}
		return anc[u][0];
	}
	int kth_ancestor(int x, int k) const {
		for(int i = 0; i < LOG; ++i) {
			if((k >> i) & 1) {
				x = anc[x][i];
				if(x == -1) return -1;
			}
		}
		return x;
	}
	vector<vector<int>> tree;
private:
	static constexpr int LOG = 20;
	vector<vector<int>> anc;
	vector<int> depth;
	void dfs(int root, int fa) {
		depth[root] = fa != -1 ? depth[fa] + 1 : 0;
		anc[root][0] = fa;
		for(int k = 1; k < LOG; ++k) {
			if(anc[root][k - 1] == -1) {
				anc[root][k] = -1;
			} else {
				anc[root][k] = anc[anc[root][k - 1]][k - 1];
			}
		}
		for(int v : tree[root]) {
			if(v != fa) {
				dfs(v, root);
			}
		}
	}
};

树链剖分

struct HLDInfo {
	ll val;
	explicit HLDInfo(ll v) : val(v) {}
	HLDInfo() : val(0) {}
	void update(HLDInfo &dst) const {
		dst.val = val;
	}
	HLDInfo operator+(const HLDInfo &x) const {
		return HLDInfo(val + x.val);
	}
};

struct Node {
	ll w;
	vector<int> e;
};
struct HLD {
	explicit HLD(const vector<Node> &g, int r = 0)
		: nodes(g.size()), nfd(g.size()) {
		dfs1(g, r, -1);
		int now_index = 0;
		dfs2(g, r, -1, now_index, r);
		vector<HLDInfo> nums(g.size());
		for(int i = 0; i < g.size(); ++i) {
			nums[nodes[i].dfn] = HLDInfo{g[i].w};
		}
		seg.assign(nums);
	}
	int lca(int u, int v) const {
		while(nodes[u].toc != nodes[v].toc) {
			if(nodes[nodes[u].toc].dep < nodes[nodes[v].toc].dep) {
				v = nodes[nodes[v].toc].fa;
			} else {
				u = nodes[nodes[u].toc].fa;
			}
		}
		return nodes[u].dep < nodes[v].dep ? u : v;
	}
	void modify(int x, ll v) {
		seg.update(nodes[x].dfn, HLDInfo{v});
	}
	ll query_sum(int u, int v) const {
		int lca_node = lca(u, v);
		auto query_to_lca = [&](int point) -> ll {
			ll ans = 0;
			for(; nodes[point].toc != nodes[lca_node].toc;
				point = nodes[nodes[point].toc].fa) {
				int pos1 = nodes[point].dfn;
				int pos2 = nodes[nodes[point].toc].dfn;
				ans += seg.query(pos2, pos1 + 1).val;
			}
			int pos1 = nodes[lca_node].dfn;
			int pos2 = nodes[point].dfn;
			ans += seg.query(pos1 + 1, pos2 + 1).val;
			return ans;
		};
		ll ans = query_to_lca(u);
		ans += query_to_lca(v);
		int lca_pos = nodes[lca_node].dfn;
		ans += seg.query(lca_pos, lca_pos + 1).val;
		return ans;
	}
private:
	struct _Node {
		int dep, fa, toc, dfn, sz, hs;
	};
	void dfs1(const vector<Node> &g, int x, int fa) {
		if(fa == -1) {
			nodes[x].dep = 0;
		} else {
			nodes[x].dep = nodes[fa].dep + 1;
		}
		nodes[x].fa = fa;
		nodes[x].sz = 1;
		nodes[x].hs = -1;
		for(int i = 0; i < g[x].e.size(); ++i) {
			int next = g[x].e[i];
			if(next == fa) continue;
			dfs1(g, next, x);
			nodes[x].sz += nodes[next].sz;
			if(nodes[x].hs == -1 ||
				nodes[nodes[x].hs].sz < nodes[next].sz) {
				nodes[x].hs = next;
			}
		}
	}
	void dfs2(const vector<Node> &g, int x, int fa, int &dfn, int toc) {
		nodes[x].dfn = dfn++;
		nodes[x].toc = toc;
		if(nodes[x].hs != -1) {
			dfs2(g, nodes[x].hs, x, dfn, toc);
			for(int i = 0; i < g[x].e.size(); ++i) {
				int next = g[x].e[i];
				if(next != nodes[x].hs && next != fa) {
					dfs2(g, next, x, dfn, next);
				}
			}
		}
	}
	vector<_Node> nodes;
	vector<int> nfd;
	SegTree<HLDInfo> seg;
};

动态BitSet

struct DynamicBitSet {
	explicit DynamicBitSet(int n = 0) : nums((n + 63) >> 6, 0), sz(n) {}
	void resize(int n) {
		nums.resize((n + 63) >> 6);
		sz = n;
	}
	bool getbit(int x) const {
		int u = x >> 6;
		int v = x - (u << 6);
		return ((nums[u] >> v) & 1) == 1;
	}
	void setbit(int x, bool val) {
		int u = x >> 6;
		int v = x - (u << 6);
		if(!val) {
			ull d = (ull)-1;
			d ^= (1ull << v);
			nums[u] &= d;
		} else {
			nums[u] |= (1ull << v);
		}
	}
	DynamicBitSet &operator&=(const DynamicBitSet &other) {
		for(int i = 0; i < nums.size(); ++i) {
			nums[i] &= other.nums[i];
		}
		return *this;
	}
	DynamicBitSet operator&(const DynamicBitSet &other) const {
		DynamicBitSet ret = *this;
		ret &= other;
		return ret;
	}
	DynamicBitSet &operator|=(const DynamicBitSet &other) {
		for(int i = 0; i < nums.size(); ++i) {
			nums[i] |= other.nums[i];
		}
		return *this;
	}
	DynamicBitSet operator|(const DynamicBitSet &other) const {
		DynamicBitSet ret = *this;
		ret |= other;
		return ret;
	}
	DynamicBitSet &operator^=(const DynamicBitSet &other) {
		for(int i = 0; i < nums.size(); ++i) {
			nums[i] ^= other.nums[i];
		}
		return *this;
	}
	DynamicBitSet operator^(const DynamicBitSet &other) const {
		DynamicBitSet ret = *this;
		ret ^= other;
		return ret;
	}
	bool allzero() const {
		for(ull i : nums) {
			if(i != 0ull) return false;
		}
		return true;
	}
	int size() const {return sz;}
private:
	vector<ull> nums;
	int sz;
};

ST表

template<class T> struct Sparse {
	using func_type = function<T(T, T)>;
	Sparse(const vector<T> &vec, func_type fn) : func(fn) {
		int n = vec.size();
		_log.resize(n + 1);
		_log[0] = -1;
		for(int i = 1; i <= n; ++i) {
			_log[i] = _log[i >> 1] + 1;
		}
		int k = _log[n] + 1;
		table.resize(n, vector<T>(k));
		for(int i = 0; i < n; ++i) {
			table[i][0] = vec[i];
		}
		for(int j = 1; j < k; ++j) {
			int step = 1 << (j - 1);
			for(int i = 0; i + step < n; ++i) {
				table[i][j] = func(table[i][j - 1], table[i + step][j - 1]);
			}
		}
	}
	T query(int l, int r) const {
		int len = r - l + 1;
		int j = _log[len];
		return func(table[l][j], table[r - (1 << j) + 1][j]);
	}
private:
	vector<vector<T>> table;
	vector<int> _log;
	func_type func;
};

01-Trie

struct Trie01 {
	Trie01() : nodes(1) {}
	void insert(ll val) {
		int now = 0;
		for(ll i = 62; i >= 0; --i) {
			ll flag = (val >> i) & 1;
			if(nodes[now].nxt[flag] == -1) {
				nodes[now].nxt[flag] = nodes.size();
				nodes.emplace_back();
			}
			now = nodes[now].nxt[flag];
		}
	}
	ll qmax_xor(ll val) const {
		int now = 0;
		ll ans = 0;
		for(ll i = 62; i >= 0; --i) {
			ll flag = (val >> i) & 1;
			if(nodes[now].nxt[1 ^ flag] != -1) {
				ans += (1ll << i);
				now = nodes[now].nxt[1 ^ flag];
			} else {
				now = nodes[now].nxt[flag];
			}
		}
		return ans;
	}
private:
	static constexpr int height = 63;
	struct Node {
		int nxt[2];
		Node() {
			nxt[0] = nxt[1] = -1;
		}
	};
	vector<Node> nodes;
};

CDQ分治

struct Data {
	int x, y, z;
	int cnt;
	int ans;
	Data(int x_, int y_, int z_) : x(x_), y(y_), z(z_), cnt(1), ans(-1) {}
};
vector<int> threed_partial(int n, int k, vector<Data> &_datas) {
	Fenwick fwk(k);
	sort(_datas.begin(), _datas.end() - 1, [](const Data &l, const Data &r) {
		if(l.x != r.x) return l.x < r.x;
		if(l.y != r.y) return l.y < r.y;
		return l.z < r.z;
	});
	// 如果值域很大,考虑离散化数据
	vector<Data> datas;
	datas.reserve(n);
	int cnt = 0;
	for(int i = 0; i < n; ++i) {
		++cnt;
		if((_datas[i].x != _datas[i + 1].x) ||
			(_datas[i].y != _datas[i + 1].y) ||
			(_datas[i].z != _datas[i + 1].z)) {
			datas.emplace_back(_datas[i].x, _datas[i].y, _datas[i].z);
			cnt = 0;
		}
	}
	int m = datas.size();
	auto cdq = [&](auto &&cdq, int l, int r) -> void {
		if(r - l < 2) {
			return;
		}
		int mid = (l + r) >> 1;
		cdq(cdq, l, mid);
		cdq(cdq, mid, r);
		sort(datas.begin() + l, datas.begin() + mid, [](const Data &l, const Data &r) {
			if(l.y != r.y) return l.y < r.y;
			return l.z < r.z;
		});
		sort(datas.begin() + mid, datas.begin() + r, [](const Data &l, const Data &r) {
			if(l.y != r.y) return l.y < r.y;
			return l.z < r.z;
		});
		int j = l;
		for(int i = mid; i < r; ++i) {
			while(datas[j].y <= datas[i].y && j < mid) {
				fwk.update(datas[j].z, datas[j].cnt);
				++j;
			}
			datas[i].ans += fwk.query(datas[i].z);
		}
		for(int k = l; k < j; ++k) {
			fwk.update(datas[k].z, -datas[k].cnt);
		}
	};
	cdq(cdq, 0, m);
	vector<int> ans(n, 0);
	for(int i = 0; i < m; ++i) {
		ans[datas[i].ans + datas[i].cnt - 1] += datas[i].cnt;
	}
	return ans;
}

莫队

struct Query {
	int idx;
	// 左闭右开
	int l, r;
	int ans;
};
// 查询区间内满足元素出现次数等于自身的数的个数
void mo_algo(vector<Query> &queries, const vector<int> &nums) {
	int n = nums.size();
	int len = sqrt(n);
	sort(queries.begin(), queries.end(), [&](const Query &ql, const Query &qr) {
		int atl = ql.l / len, atr = qr.l / len;
		if(atl != atr) {
			return atl < atr;
		}
		return (bool)((atl % 2 == 0) ^ (ql.r < qr.r));
	});
	int l = 0, r = 0, ans = 0;
	vector<int> count(n + 5, 0);
	auto add = [&](int x) {
		if(x >= n + 5) return;
		if(count[x] == x) --ans;
		count[x]++;
		if(count[x] == x) ++ans;
	};
	auto remove = [&](int x) {
		if(x >= n + 5) return;
		if(count[x] == x) --ans;
		count[x]--;
		if(count[x] == x) ++ans;
	};
	for(auto &q : queries) {
		// 抄板子时注意顺序,先加再更新还是先更新再加
		while(l > q.l) {
			--l;
			add(nums[l]);
		}
		while(l < q.l) {
			remove(nums[l]);
			++l;
		}
		while(r < q.r) {
			add(nums[r]);
			++r;
		}
		while(r > q.r) {
			--r;
			remove(nums[r]);
		}
		q.ans = ans;
	}
	sort(queries.begin(), queries.end(), [](const Query &l, const Query &r) {
		return l.idx < r.idx;
	});
}

单调栈、单调队列

// 单调栈:[l, r),且有相同元素时,左边取最左端,右边取下一个自己
vector<pair<int, int>> max_range(const vector<int> &nums) {
	int n = nums.size();
	vector ret(n, pair(-1, -1));
	vector<pair<int, int>> stk; // 单调栈
	ret[0].first = 0;
	stk.emplace_back(nums[0], 0);
	for(int i = 1; i < n; ++i) {
		while(!stk.empty() && stk.back().first <= nums[i]) {
			ret[stk.back().second].second = i;
			stk.pop_back();
		}
		ret[i].first = stk.empty() ? 0 : stk.back().second + 1;
		stk.emplace_back(nums[i], i);
	}
	for(int i = n - 1; i >= 0; --i) {
		if(ret[i].second == -1) {
			ret[i].second = n;
		}
	}
	return ret;
}
// 可以有相同元素
	vector ret(n, pair(0, n));
	vector<int> stk;
	for(int i = 0; i < n; ++i) {
		while(!stk.empty() && nums[stk.back()] <= nums[i]) {
			stk.pop_back();
		}
		if(!stk.empty()) {
			ret[i].first = stk.back() + 1;
		}
		stk.push_back(i);
	}
	stk.clear();
	for(int i = n - 1; i >= 0; --i) {
		while(!stk.empty() && nums[stk.back()] <= nums[i]) {
			stk.pop_back();
		}
		if(!stk.empty()) {
			ret[i].second = stk.back();
		}
		stk.push_back(i);
	}
// 最大滑动窗口:单调队列
vector<int> max_sliding_window(const vector<int> &nums, int k) {
	int n = nums.size();
	vector<int> ret(n - k + 1);
	vector<pair<int, int>> que;
	for(int i = 0; i < k - 1; ++i) {
		while(!que.empty() && que.back().first < nums[i]) {
			que.pop_back();
		}
		que.emplace_back(nums[i], i);
	}
	int st = 0;
	for(int i = 0; i <= n - k; ++i) {
		int j = i + k - 1;
		if(que.size() > st && que[st].second < i) {
			++st;
		}
		while(!que.empty() && que.back().first < nums[j]) {
			que.pop_back();
		}
		st = min(st, (int)que.size());
		que.emplace_back(nums[j], j);
		ret[i] = que[st].first;
	}
	return ret;
}

图论

Dijkstra

constexpr ll inf = 0x3f3f3f3f3f3f3f3f;
vector<ll> dijkstra(vector<vector<pair<int, ll>>> &graph, int start) {
	int v = graph.size();
	vector<ll> dist(v, inf);
	dist[start] = 0;
	vector<bool> visited(v, false);
	priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<>> pq;
	for(auto [vtx, w] : graph[start]) {
		dist[vtx] = w;
		pq.emplace(w, vtx);
	}
	while(!pq.empty()) {
		auto [w, vtx] = pq.top();
		pq.pop();
		if(visited[vtx]) continue;
		visited[vtx] = true;
		for(auto [vt, ww] : graph[vtx]) {
			if(!visited[vt]) {
				if(dist[vt] > dist[vtx] + ww) {
					dist[vt] = dist[vtx] + ww;
					pq.emplace(dist[vt], vt);
				}
			}
		}
	}
	return dist;
}

Floyd

// 操作后,graph就变成了最短路
void floyd(vector<vector<ll>> &graph) {
	for(int k = 0; k < graph.size(); ++k) {
		for(int i = 0; i < graph.size(); ++i) {
			for(int j = 0; j < graph.size(); ++j) {
				if(graph[i][j] > graph[i][k] + graph[k][j]) {
					graph[i][j] = graph[i][k] + graph[k][j];
				}
			}
		}
	}
}

SPFA

constexpr ll inf = 0x3f3f3f3f3f3f3f3f;
// 返回空vector说明有负环
vector<ll> spfa(const vector<vector<pair<int, ll>>> &graph, int start) {
	int n = graph.size();
	vector<ll> dist(n, inf);
	vector<int> cnt(n, 0);
	vector<bool> inq(n, false);
	queue<int> q;
	q.push(start);
	dist[start] = 0;
	inq[start] = true;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		inq[u] = false;
		for(auto [v, w] : graph[u]) {
			if(dist[v] > dist[u] + w) {
				dist[v] = dist[u] + w;
				if(!inq[v]) {
					inq[v] = true;
					q.push(v);
					cnt[v] = cnt[u] + 1;
					if(cnt[v] > n) return {};
				}
			}
		}
	}
	return dist;
}

或值最短路

int or_mindist(vector<tuple<int, int, int>> &edges, int n) {
	int ans = (1 << 30) - 1;
	for(int i = 29; i >= 0; i--) {
		ans ^= (1 << i);
		DSU dsu(n);
		for(auto &[u, v, w] : edges) {
			if((w & ans) == w) {
				dsu.connect(u, v);
			}
		}
		if(!dsu.is_connected(0, n - 1)) {
			ans ^= (1 << i);
		}
	}
	return ans;
}

Tarjan全家桶

// 强连通分量
struct SCC {
	explicit SCC(int n) : nodes(n), graph(n) {}
	void addedge(int u, int v) {
		if(u == v) return;
		graph[u].push_back(v);
	}
	void solve() {
		int dfn = 0, cnt = 0;
		for(int i = 0; i < nodes.size(); i++) {
			if(nodes[i].dfn == -1) {
				dfs(dfn, cnt, i);
			}
		}
		dag.assign(cnt, {});
		set<pair<int, int>> edges;
		for(int u = 0; u < graph.size(); ++u) {
			for(int v : graph[u]) {
				int bu = nodes[u].inscc, bv = nodes[v].inscc;
				if(bu != bv && !edges.contains({ bu, bv })) {
					dag[bu].emplace_back(bv);
					edges.insert({ bu, bv });
				}
			}
		}
	}
	struct Node {
		int dfn;
		int low;
		bool ins;
		int inscc;
		Node() : dfn(-1), low(-1), ins(false), inscc(-1) {}
	};
	vector<Node> nodes;
	vector<vector<int>> graph;
	vector<vector<int>> sccs;
	vector<vector<int>> dag;
private:
	stack<int> scc_stack;
	void dfs(int &dfn, int &cnt, int u) {
		nodes[u].dfn = dfn;
		nodes[u].low = dfn;
		++dfn;
		scc_stack.push(u);
		nodes[u].ins = true;
		for(int v : graph[u]) {
			if(nodes[v].dfn == -1) {
				dfs(dfn, cnt, v);
				nodes[u].low = min(nodes[u].low, nodes[v].low);
			} else if(nodes[v].ins) {
				nodes[u].low = min(nodes[u].low, nodes[v].low);
			}
		}
		if(nodes[u].dfn == nodes[u].low) {
			vector<int> scc;
			int v = -1;
			while(v != u) {
				v = scc_stack.top();
				scc_stack.pop();
				nodes[v].ins = false;
				nodes[v].inscc = cnt;
				scc.emplace_back(v);
			}
			sccs.emplace_back(move(scc));
			++cnt;
		}
	}
};
// 边双
struct EBCC {
	explicit EBCC(int n) : nodes(n), graph(n), in_ebcc(n) {}
	void addedge(int u, int v) {
		if(u == v)
			return;
		graph[u].emplace_back(v);
		graph[v].emplace_back(u);
	}
	void solve() {
		int dfn = 0;
		for(int i = 0; i < nodes.size(); ++i) {
			if(nodes[i].dfn == -1) {
				dfs(i, -1, dfn);
			}
		}
		for(int i = 0; i < ebcc.size(); ++i) {
			for(int u : ebcc[i]) {
				in_ebcc[u] = i;
			}
		}
	}
	vector<vector<int>> graph;
	vector<vector<int>> ebcc;
	vector<int> in_ebcc;
private:
	struct Node {
		int dfn;
		int low;
		bool ins;
		Node() : dfn(-1), low(-1), ins(false) {}
	};
	vector<Node> nodes;
	stack<int> stk;
	void dfs(int u, int fa, int &dfn) {
		nodes[u].dfn = nodes[u].low = dfn;
		nodes[u].ins = true;
		dfn++;
		stk.push(u);
		for(int v : graph[u]) {
			if(v == fa)
				continue;
			if(nodes[v].dfn == -1) {
				dfs(v, u, dfn);
				nodes[u].low = min(nodes[u].low, nodes[v].low);
			} else if(nodes[v].ins) {
				nodes[u].low = min(nodes[u].low, nodes[v].dfn);
			}
		}
		if(nodes[u].dfn == nodes[u].low) {
			vector<int> t;
			int n = -1;
			while(n != u) {
				n = stk.top();
				stk.pop();
				t.emplace_back(n);
				nodes[n].ins = false;
			}
			ebcc.emplace_back(move(t));
		}
	}
};
// 点双
struct DCC {
	explicit DCC(int n)
		: nodes(n), graph(n) {}
	void addedge(int u, int v) {
		if(u == v) return;
		graph[u].emplace_back(v);
		graph[v].emplace_back(u);
	}
	void solve() {
		int dfn_now = 0;
		for(int i = 0; i < nodes.size(); ++i) {
			if(nodes[i].dfn == -1) {
				dfs(i, -1, i, dfn_now);
			}
		}
	}
	struct Node {
		int dfn;
		int low;
		Node() :dfn(-1), low(-1) {}
	};
	vector<Node> nodes;
	vector<vector<int>> graph;
	vector<vector<int>> dcc;
private:
	stack<int> stk;
	void dfs(int u, int parent, int root, int &dfn_now) {
		nodes[u].dfn = dfn_now;
		nodes[u].low = dfn_now;
		dfn_now++;
		stk.push(u);
		int child = 0;
		for(int v : graph[u]) {
			if(v == parent) continue;
			if(nodes[v].dfn == -1) {
				dfs(v, u, root, dfn_now);
				nodes[u].low = min(nodes[u].low, nodes[v].low);
				if(nodes[v].low >= nodes[u].dfn) {
					++child;
					vector<int> f = { u };
					while(!stk.empty()) {
						int x = stk.top();
						stk.pop();
						f.emplace_back(x);
						if(x == v) break;
					}
					dcc.emplace_back(move(f));
				}
			} else {
				nodes[u].low = min(nodes[u].low, nodes[v].dfn);
			}
		}
		if(u == root && graph[u].empty()) {
			dcc.emplace_back(vector<int>{u});
			return;
		}
	}
};
// 割点
struct Cutpoint {
	explicit Cutpoint(int n) : nodes(n), graph(n) {}
	void addedge(int u, int v) {
		graph[u].emplace_back(v);
		graph[v].emplace_back(u);
	}
	void solve() {
		int dfn = 0;
		for(int i = 0; i < nodes.size(); ++i) {
			if(nodes[i].dfn == -1) {
				dfs(i, -1, dfn);
			}
		}
		sort(cutpoints.begin(), cutpoints.end());
		cutpoints.erase(unique(cutpoints.begin(), cutpoints.end()), cutpoints.end());
	}
	struct Node {
		int dfn, low;
		Node() : dfn(-1), low(-1) {}
	};
	vector<Node> nodes;
	vector<vector<int>> graph;
	vector<int> cutpoints;
private:
	void dfs(int u, int fa, int &dfn) {
		nodes[u].dfn = nodes[u].low = dfn;
		++dfn;
		int child = 0;
		bool flag = false;
		for(int v : graph[u]) {
			if(nodes[v].dfn == -1) {
				++child;
				dfs(v, u, dfn);
				nodes[u].low = min(nodes[u].low, nodes[v].low);
				if(fa != -1) {
					flag |= (nodes[v].low >= nodes[u].dfn);
				}
			} else if(v != fa) {
				nodes[u].low = min(nodes[u].low, nodes[v].dfn);
			}
		}
		if(fa == -1) {
			flag = (child > 1);
		}
		if(flag) {
			cutpoints.emplace_back(u);
		}
	}
};
// 桥
struct Bridge {
	explicit Bridge(int n)
		: nodes(n), graph(n) {}
	void addedge(int u, int v) {
		graph[u].emplace_back(v);
		graph[v].emplace_back(u);
	}
	void solve() {
		int dfn_now = 0;
		for(int i = 0; i < nodes.size(); ++i) {
			if(nodes[i].dfn == -1) {
				dfs(i, -1, dfn_now);
			}
		}
	}
	struct Node {
		int dfn;
		int low;
		Node() : dfn(-1), low(-1) {}
	};
	vector<Node> nodes;
	vector<vector<int>> graph;
	vector<pair<int, int>> bridges;
private:
	void dfs(int u, int father, int &dfn_now) {
		nodes[u].dfn = nodes[u].low = dfn_now++;
		for(int v : graph[u]) {
			if(nodes[v].dfn == -1) {
				dfs(v, u, dfn_now);
				nodes[u].low = min(nodes[u].low, nodes[v].low);
				if(nodes[v].low > nodes[u].dfn) {
					bridges.emplace_back(min(u, v), max(u, v));
				}
			} else if(v != father) {
				nodes[u].low = min(nodes[u].low, nodes[v].dfn);
			}
		}
	}
};

同余最短路

// 跑的dijkstra
ll remainder_mindist(ll k, vector<ll> dist) {
	ll modulo = 2 * min(dist[0], dist[3]);
	vector<vector<ll>> dp(4, vector<ll>(modulo, inf));
	vector<vector<bool>> visited(4, vector<bool>(modulo, false));
	dp[0][0] = 0;
	priority_queue<tuple<ll, ll, ll>, vector<tuple<ll, ll, ll>>, greater<>> pq;
	pq.emplace(0, 0, 0);
	auto get = [](ll a, ll b) -> ll {
		if(a == 3 && b == 0) {
			return 3;
		}
		if(b == 3 && a == 0) {
			return 3;
		}
		return min(a, b);
	};
	while(!pq.empty()) {
		auto [w, m, c] = pq.top();
		pq.pop();
		if(visited[c][m]) continue;
		visited[c][m] = true;
		ll d1 = (c + 1) % 4, d2 = (c + 3) % 4;
		ll w1 = dist[get(c, d1)], w2 = dist[get(c, d2)];
		if(chkmin(dp[d1][(w + w1) % modulo], w + w1)) {
			pq.emplace(w + w1, (w + w1) % modulo, d1);
		}
		if(chkmin(dp[d2][(w + w2) % modulo], w + w2)) {
			pq.emplace(w + w2, (w + w2) % modulo, d2);
		}
	}
	ll ans = inf;
	for(ll i = 0; i < modulo; ++i) {
		ll required = (k / modulo) * modulo + i;
		while(required < k) {
			required += modulo;
		}
		chkmin(ans, max(required, dp[0][i]));
	}
	return ans;
}

Kruskal

constexpr ll inf = 0x3f3f3f3f3f3f3f3f;
struct Edge {
	int u, v;
	ll w;
};
// 拿个并查集板子下来谢谢喵
ll kruskal(vector<Edge> &edges, int n) {
	DSU dsu(n);
	ll ans = 0;
	sort(edges.begin(), edges.end(), [](const Edge &a, const Edge &b) {
		return a.w < b.w;
	});
	for(auto &e : edges) {
		if(!dsu.is_connected(e.u, e.v)) {
			dsu.connect(e.u, e.v);
			ans += e.w;
		}
	}
	for(int i = 1; i < n; ++i) {
		if(!dsu.is_connected(i, 0)) {
			return inf;
		}
	}
	return ans;
}

Prim

constexpr ll inf = 0x3f3f3f3f3f3f3f3f;
ll prim(const vector<vector<pair<int, ll>>> &graph) {
	int n = graph.size();
	vector<bool> visited(n, false);
	vector<ll> dist(n, inf);
	dist[0] = 0;
	priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<>> pq;
	pq.emplace(0, 0);
	ll ret = 0;
	while(!pq.empty()) {
		auto [w, v] = pq.top();
		pq.pop();
		if(visited[v]) continue;
		visited[v] = true;
		ret += w;
		for(auto [u, w] : graph[v]) {
			if(!visited[u] && dist[u] > w) {
				dist[u] = w;
				pq.emplace(w, u);
			}
		}
	}
	for(bool b : visited) {
		if(!b) {
			return inf;
		}
	}
	return ret;
}

Boruvka

constexpr ll inf = 0x3f3f3f3f3f3f3f3f;
struct Edge {
	int u, v;
	ll w;
};
ll boruvka(const vector<Edge> &edges, int n) {
	DSU dsu(n);
	ll ans = 0;
	int c = n;
	vector<Edge> mst(n);
	while(c > 1) {
		for(int i = 0; i < n; ++i) {
			if(dsu.find(i) != i) continue;
			mst[i] = Edge(-1, -1, inf);
		}
		for(auto [u, v, w] : edges) {
			int fu = dsu.find(u), fv = dsu.find(v);
			if(fu == fv) continue;
			if(mst[fu].w > w) {
				mst[fu] = Edge(u, v, w);
			}
			if(mst[fv].w > w) {
				mst[fv] = Edge(u, v, w);
			}
		}
		bool flag = false;
		for(int i = 0; i < n; ++i) {
			if(dsu.find(i) != i) continue;
			auto [u, v, w] = mst[i];
			if(w == inf) return inf;
			if(!dsu.is_connected(u, v)) {
				dsu.connect(u, v);
				ans += w;
				--c;
				flag = true;
			}
		}
		if(!flag) {
			return inf;
		}
	}
	return ans;
}

Dinic

constexpr ll inf = 0x3f3f3f3f3f3f3f3f;
struct Dinic {
	struct Edge {
		int to, rev;
		ll cap;
	};
	vector<vector<Edge>> graph;
	Dinic(int n, int s, int e)
		: graph(n), level(n), start(s), end(e), n(n) {}
	void addedge(int u, int v, ll w) {
		graph[u].emplace_back(Edge{ v, (int)graph[v].size(), w });
		graph[v].emplace_back(Edge{ u, (int)graph[u].size() - 1, 0 });
	}
	ll solve() {
		ll ans = 0;
		while(bfs()) {
			ll flow = dfs(start, inf);
			while(flow > 0) {
				ans += flow;
				flow = dfs(start, inf);
			}
		}
		return ans;
	}
private:
	vector<int> level;
	int n, start, end;
	bool bfs() {
		fill(level.begin(), level.end(), -1);
		level[start] = 0;
		queue<int> q;
		q.emplace(start);
		while(!q.empty()) {
			int u = q.front();
			q.pop();
			for(const auto &e : graph[u]) {
				if(level[e.to] == -1 && e.cap > 0) {
					level[e.to] = level[u] + 1;
					q.emplace(e.to);
				}
			}
		}
		return level[end] != -1;
	}
	ll dfs(int u, ll mf) {
		if(u == end || mf == 0) return mf;
		ll nf = 0;
		for(auto &e : graph[u]) {
			if(level[e.to] == level[u] + 1 && e.cap > 0) {
				ll min_flow = min(mf, e.cap);
				ll push = dfs(e.to, min_flow);
				if(push > 0) {
					e.cap -= push;
					graph[e.to][e.rev].cap += push;
					nf += push;
					mf -= push;
					if(mf == 0) {
						return nf;
					}
				}
			}
		}
		return nf;
	}
};

最小费用最大流

EK

struct EK {
	struct Edge {
		int dst, rev;
		ll weight, flow;
		bool neg;
	};
	vector<vector<Edge>> graph;
	int n;
	explicit EK(int _n) : n(_n), graph(_n) {}
	void addedge(int u, int v, ll flow, ll weight) {
		int iu = graph[u].size(), iv = graph[v].size();
		graph[u].push_back({ v, iv, weight, flow, false });
		graph[v].push_back({ u, iu, -weight, 0, true });
	}
	pair<ll, ll> mcmf(int s, int t) {
		ll cost = 0, flow = 0;
		while(true) {
			vector<int> pv(n, -1), pe(n, -1);
			vector<ll> d(n, inf), mf(n, 0);
			vector<bool> inq(n, false);
			queue<int> q;
			q.push(s);
			d[s] = 0;
			mf[s] = inf;
			inq[s] = true;
			while(!q.empty()) {
				int u = q.front();
				q.pop();
				inq[u] = false;
				for(int i = 0; i < graph[u].size(); ++i) {
					auto &e = graph[u][i];
					int v = e.dst;
					ll f = e.flow, w = e.weight;
					if(f > 0 && d[u] + w < d[v]) {
						d[v] = d[u] + w;
						pv[v] = u;
						pe[v] = i;
						mf[v] = min(mf[u], f);
						if(!inq[v]) {
							q.push(v);
							inq[v] = true;
						}
					}
				}
			}
			if(mf[t] <= 0) break;
			ll add = mf[t];
			for(int v = t; v != s; v = pv[v]) {
				int u = pv[v], e = pe[v];
				graph[u][e].flow -= add;
				graph[v][graph[u][e].rev].flow += add;
			}
			flow += add;
			cost += add * d[t];
		}
		return { cost, flow };
	}
};

匈牙利算法(不带权)

int hungarian(const vector<vector<int>> &graph, int vsz) {
	int usz = graph.size();
	vector<int> mu(usz, -1);
	vector<int> mv(vsz, -1);
	auto dfs = [&](auto &&dfs, int u, vector<bool> &visited) -> bool {
		for(int v : graph[u]) {
			if(visited[v]) continue;
			visited[v] = true;
			if(mv[v] == -1 || dfs(dfs, mv[v], visited)) {
				mv[v] = u;
				mu[u] = v;
				return true;
			}
		}
		return false;
	};
	int ret = 0;
	for(int u = 0; u < usz; ++u) {
		if(mu[u] == -1) {
			vector<bool> visited(vsz, false);
			if(dfs(dfs, u, visited)) {
				ret++;
			}
		}
	}
	return ret;
}

2-SAT

// 本题给的是析取式,在这里我转化为了蕴含式求解
// 请打一个SCC下来
// 注意:蕴含式也要加另一条边,A->B要加B'->A'
vector<int> solve_twosat(int n, const vector<tuple<int, int, int, int>> &conds) {
	SCC solver(2 * n);
	for(auto [i, flag_i, j, flag_j] : conds) {
		solver.addedge(2 * i + 1 - flag_i, 2 * j + flag_j);
		solver.addedge(2 * j + 1 - flag_j, 2 * i + flag_i);
	}
	solver.solve();
	for(int i = 0; i < n; ++i) {
		if(solver.nodes[2 * i].inscc == solver.nodes[2 * i + 1].inscc) {
			return {};
		}
	}
	vector<int> ans(n, -1);
	for(int i = 0; i < solver.sccs.size(); ++i) {
		for(int ii : solver.sccs[i]) {
			int u = ii / 2;
			int v = ii % 2;
			if(ans[u] == -1) {
				ans[u] = v;
			}
		}
	}
	return ans;
}

搜索

迭代加深

pair<int, stack<int>> iddfs(array<array<int, 3>, 3> src, const array<array<int, 3>, 3> &dst) {
	constexpr int dx[] = { -1, 1, 0, 0 };
	constexpr int dy[] = { 0, 0, -1, 1 };
	int ans = 0;
	stack<int> stk;
	pair<int, int> zeropos;
	for(int i = 0; i < 3; ++i) {
		for(int j = 0; j < 3; ++j) {
			if(src[i][j] == 0) {
				zeropos = { i, j };
			}
		}
	}
	auto dfs = [&](auto &&dfs, int depth, int lastdir) -> bool {
		if(src == dst) {
			for(int i = 0; i < 3; ++i) {
				for(int j = 0; j < 3; ++j) {
					stk.push(src[i][j]);
				}
			}
			return true;
		}
		if(depth >= ans) return false;
		auto revdir = [](int cur) -> int {
			if(cur < 2) return 1 - cur;
			return 5 - cur;
		};
		for(int nowdir = 0; nowdir < 4; ++nowdir) {
			if(nowdir == revdir(lastdir)) {
				continue;
			}
			auto [oldx, oldy] = zeropos;
			int newx = oldx + dx[nowdir], newy = oldy + dy[nowdir];
			if(newx < 0 || newx > 2 || newy < 0 || newy > 2) continue;
			swap(src[oldx][oldy], src[newx][newy]);
			zeropos = { newx, newy };
			if(dfs(dfs, depth + 1, nowdir)) {
				swap(src[oldx][oldy], src[newx][newy]);
				zeropos = { oldx, oldy };
				for(int i = 0; i < 3; ++i) {
					for(int j = 0; j < 3; ++j) {
						stk.push(src[i][j]);
					}
				}
				return true;
			}
			swap(src[oldx][oldy], src[newx][newy]);
			zeropos = { oldx, oldy };
		}
		return false;
	};
	for(ans = 0; ans < 33; ++ans) {
		if(dfs(dfs, 0, 114514)) {
			return { ans, stk };
		}
	}
	return { -1, stack<int>{} };
}

动态规划

背包DP

namespace OneD {
// 01背包
ll knapsack_01(const vector<pair<int, ll>> &objects, int maxw) {
	int n = objects.size();
	vector<ll> dp(maxw + 1, 0);
	for(auto [w, v] : objects) {
		for(int j = maxw; j >= w; --j) {
			dp[j] = max(dp[j], dp[j - w] + v);
		}
	}
	return dp[maxw];
}
// 完全背包
ll knapsack_complete(const vector<pair<int, ll>> &objects, int maxw) {
	int n = objects.size();
	vector<ll> dp(maxw + 1, 0);
	for(auto [w, v] : objects) {
		for(int j = w; j <= maxw; ++j) {
			dp[j] = max(dp[j], dp[j - w] + v);
		}
	}
	return dp[maxw];
}
struct Object {
	int cost, cnt;
	ll w;
};
// 多重背包
ll multi_knapsack(const vector<Object> &objects, int maxw) {
	vector<ll> dp(maxw + 1, 0);
	for(auto [cost, cnt, w] : objects) {
		for(int _ = 0; _ < cnt; ++_) {
			for(int j = maxw; j >= cost; --j) {
				dp[j] = max(dp[j], dp[j - cost] + w);
			}
		}
	}
	return dp[maxw];
}
// 多重背包的二进制优化
ll multi_knapsack_binary(const vector<Object> &objects, int maxw) {
	vector<pair<int, ll>> objs;
	for(auto [cost, cnt, w] : objects) {
		int k = 1;
		while(cnt > 0) {
			int take = min(k, cnt);
			objs.emplace_back(cost * take, w * take);
			cnt -= take;
			k *= 2;
		}
	}
	return knapsack_01(objs, maxw);
}
// 缺少:多重背包的单调队列优化
}

namespace TwoD {
struct Object {
	int u, v;
	ll w;
};
// 二维01背包
ll knapsack_01(int U, int V, const vector<Object> &objects) {
	vector<vector<ll>> dp(U + 1, vector<ll>(V + 1, 0));
	for(auto [u, v, w] : objects) {
		for(int i = U; i - u >= 0; --i) {
			for(int j = V; j - v >= 0; --j) {
				dp[i][j] = max(dp[i][j], dp[i - u][j - v] + w);
			}
		}
	}
	return dp[U][V];
}

}

区间DP

int main() {
	cin.tie(nullptr)->sync_with_stdio(false);
	int n;
	cin >> n;
	vector<ll> nums(n);
	for(int i = 0; i < n; ++i) {
		cin >> nums[i];
	}
	vector<int> s(2 * n + 1);
	for(int i = 0; i < 2 * n; ++i) {
		s[i + 1] = s[i] + nums[i];
	}
	vector<vector<int>> dp(2 * n + 1, vector<int>(2 * n + 1));
	for(int len = 2; len <= n; ++len) {
		for(int i = 0; i <= 2 * n - len; ++i) {
			int j = i + len;
			int mx = 0;
			for(int k = i + 1; k < j; ++k) {
				mx = max(mx, dp[i][k] + dp[k][j]);
			}
			dp[i][j] = mx + s[j] - s[i];
		}
	}
	int ans = 0;
	for(int i = 0; i < n; ++i) {
		ans = max(ans, dp[i][i + n]);
	}
	cout << ans << '\n';
	return 0;
}

数位DP

constexpr ll modulo = 10'0000'0007;
ll digit_dp(string k, int d) {
	vector<vector<ll>> memo(k.size(), vector<ll>(d, -1));
	auto dfs = [&](auto &&dfs, int idx, ll psum, bool isnum, bool islimit) -> ll {
		if(idx == k.size()) {
			return (ll)(isnum && (psum == 0));
		}
		if(memo[idx][psum] != -1 && isnum && !islimit) {
			return memo[idx][psum];
		}
		ll ans = 0;
		if(!isnum) {
			ans = (ans + dfs(dfs, idx + 1, 0, false, false)) % modulo;
		}
		int llim = isnum ? 0 : 1, hlim = islimit ? (k[idx] - '0') : 9;
		for(int i = llim; i <= hlim; ++i) {
			ans = (ans + dfs(dfs, idx + 1, ((psum + i) % d),
				true, (islimit && (i == hlim)))) % modulo;
		}
		if(isnum && !islimit) {
			memo[idx][psum] = ans;
		}
		return ans;
	};
	return dfs(dfs, 0, 0, false, true);
}

计数DP

constexpr ll modulo = 10'0000'0007;
ll count_dp(string s) {
	int n = s.size();
	vector<vector<ll>> dp(n, vector<ll>(n, 0));
	dp[0][0] = 1;
	vector<vector<ll>> sum(n, vector<ll>(n + 1, 0));
	sum[0][1] = 1;
	for(int i = 1; i < n; ++i) {
		for(int j = 0; j <= i; ++j) {
			if(s[i - 1] == '<') {
				dp[i][j] = sum[i - 1][j];
			} else {
				dp[i][j] = (sum[i - 1][i] - sum[i - 1][j] + modulo) % modulo;
			}
		}
		sum[i][0] = 0;
		for(int j = 1; j <= i + 1; ++j) {
			sum[i][j] = (sum[i][j - 1] + dp[i][j - 1]) % modulo;
		}
	}
	ll ans = 0;
	for(int j = 0; j < n; ++j) {
		ans = (ans + dp[n - 1][j]) % modulo;
	}
}

线段树优化DP

// SegTree区间最大值、单点加法线段树
int lis(const vector<int> &nums) {
	auto discrete = nums;
	sort(discrete.begin(), discrete.end());
	discrete.erase(unique(discrete.begin(), discrete.end()), discrete.end());
	unordered_map<int, int> mp;
	for(int i = 0; i < discrete.size(); i++) {
		mp[discrete[i]] = i;
	}
	SegTree seg(discrete.size());
	for(int i = 0; i < nums.size(); i++) {
		int x = mp[nums[i]];
		int v = seg.query(0, x) + 1;
		seg.update(x, v);
	}
	return seg.query(0, discrete.size());
}

数学

快速幂和模整数类

ll qpow(ll x, ll n) {
	ll ret = 1;
	for(; n != 0; n >>= 1, x = x * x % modulo) {
		if(n & 1) ret = ret * x % modulo;
	}
	return ret;
}

struct PreprocessedPow {
	PreprocessedPow(ll k, ll maxn) {
		k %= modulo;
		m = ceil(sqrt(maxn + 1.5));
		powerk.assign(m, 1);
		powerkm.assign(m, 1);
		for(int i = 1; i < m; ++i) {
			powerk[i] = (ll(powerk[i - 1]) * k) % modulo;
		}
		powerkm[1] = ll(powerk[m - 1]) * k % modulo;
		for(int i = 2; i < m; ++i) {
			powerkm[i] = ll(powerkm[i - 1]) * ll(powerkm[1]) % modulo;
		}
	}
	ll pow(ll n) const {
		ll i = n / m, j = n % m;
		return (ll(powerkm[i]) * ll(powerk[j])) % modulo;
	}
private:
	int m;
	vector<int> powerk, powerkm;
};

struct ModInt {
	ModInt(ll v = 0) : val(v % modulo) {
		if(val < 0) val += modulo;
	}
	ModInt operator+(const ModInt &rhs) const {
		ll retval = val + rhs.val;
		if(retval >= modulo) retval -= modulo;
		return ModInt(retval, true);
	}
	ModInt operator-(const ModInt &rhs) const {
		ll retval = val - rhs.val;
		if(retval < 0) retval += modulo;
		return ModInt(retval, true);
	}
	ModInt operator*(const ModInt &rhs) const {
		return ModInt(val * rhs.val % modulo, true);
	}
	ModInt operator/(const ModInt &rhs) const {
		return ModInt(val * qpow(rhs.val, modulo - 2) % modulo, true);
	}
	ModInt power(int n) const {
		ModInt ret = 1;
		for(ModInt base = val; n != 0; n >>= 1, base = base * base) {
			if(n & 1) ret = ret * base;
		}
		return ret;
	}
	operator ll() const {
		return val;
	}
private:
	ll val;
	ModInt(ll v, bool) : val(v) {}
};

组合数

struct Comb {
	Comb() = delete;
	static ll fact(ll n) {
		return _fact[n];
	}
	static ll invfact(ll n) {
		return _invfact[n];
	}
	static ll binom(ll n, ll m) {
		if(m < 0 || m > n) return 0;
		return (((ll(_fact[n]) * ll(_invfact[m])) % modulo)
			* ll(_invfact[n - m])) % modulo;
	}
private:
	static constexpr int _maxn = 500005;
	static inline int _fact[_maxn], _invfact[_maxn];
	static inline int init = [&] {
		_fact[0] = 1;
		for(ll i = 1; i < _maxn; ++i) {
			_fact[i] = (ll(_fact[i - 1]) * i) % modulo;
		}
		_invfact[_maxn - 1] = qpow(_fact[_maxn - 1], modulo - 2);
		for(ll i = _maxn - 2; i >= 0; --i) {
			_invfact[i] = (ll(_invfact[i + 1]) * (i + 1)) % modulo;
		}
		return 0;
	}();
};

Lucas定理求组合数

struct Lucas {
	explicit Lucas(int m) : modulo(m), fact(m + 1, 1), invfact(m + 1, 1) {
		for(ll i = 1; i <= m; ++i) {
			fact[i] = (ll)fact[i - 1] * i % modulo;
		}
		invfact[m] = qpow(fact[m], modulo - 2);
		for(ll i = m - 1; i >= 0; --i) {
			invfact[i] = ll(invfact[i + 1]) * (i + 1) % modulo;
		}
	}
	int binom(ll n, ll m) const {
		auto _binom = [&](int n, int m) -> ll {
			if(m < 0 || m > n) return 0;
			return ll(fact[n]) * ll(invfact[m]) * ll(invfact[n - m]) % modulo;
		};
		if(m == 0) return 1;
		return ll(binom(n / modulo, m / modulo))
			* _binom(n % modulo, m % modulo) % modulo;
	}
private:
	int modulo;
	vector<int> fact, invfact;
	ll qpow(ll x, ll n) const {
		ll ret = 1;
		for(; n != 0; n >>= 1, x = x * x % modulo) {
			if(n & 1) ret = ret * x % modulo;
		}
		return ret;
	}
};

卡特兰数

struct Catalan {
	Catalan() = delete;
	static ll get(int x) {
		return _cat[x];
	}
private:
	static constexpr int _maxn = 250005;
	static inline int _cat[_maxn];
	static inline int init = [] {
		_cat[0] = 1;
		for(int i = 1; i < _maxn; ++i) {
			_cat[i] = (Comb::binom(2 * i, i)
				- Comb::binom(2 * i, i - 1) + modulo) % modulo;
		}
		return 0;
	}();
};

错位排列

struct Dislocation {
	Dislocation() = delete;
	static ll get(int i) {
		return nums[i];
	}
private:
	static constexpr ll maxn = 500005;
	static inline ll nums[maxn];
	static inline int init = [] {
		nums[0] = nums[1] = nums[2] = 1;
		for(ll i = 3; i < maxn; ++i) {
			nums[i] = (i - 1) * (nums[i - 1] + nums[i - 2]) % modulo;
		}
		return 0;
	}();
};

离散对数

// returns ret so that qpow(ret, a) = b, -1 if not exist
ll mlog(ll a, ll b) {
	if(b == 1) return 0;
	ll t = ceil(sqrt(modulo));
	ll now = b;
	unordered_map<ll, ll, MyHash> table;
	for(int i = 0; i < t; ++i) {
		table[now] = i;
		now = now * a % modulo;
	}
	ll mi = qpow(a, t);
	now = 1;
	for(int i = 1; i <= t; ++i) {
		now = now * mi % modulo;
		if(table.contains(now)) {
			return i * t - table[now];
		}
	}
	return -1;
}

EXGCD

// @returns (gcd, x, y) so that gcd = ax + by
tuple<ll, ll, ll> exgcd(ll a, ll b) {
	if(b == 0) return tuple(a, 1ll, 0ll);
	auto [g, x, y] = exgcd(b, a % b);
	return tuple(g, y, x - (a / b) * y);
}

中国剩余定理

// @returns (a, b) so that answer is a + kb, k\in N_+
pair<ll, ll> crt(const vector<ll> &rem, const vector<ll> &mod) {
	int n = rem.size();
	ll modulo_ = 1, ans = 0;
	for(int i = 0; i < n; ++i) {
		modulo_ *= mod[i];
	}
	for(int i = 0; i < n; ++i) {
		ll m = modulo_ / mod[i];
		auto [_, b, __] = exgcd(m, mod[i]);
		ans = (ans + ((rem[i] * m) % modulo_ * b) % modulo_) % modulo_;
	}
	return pair((ans % modulo_ + modulo_) % modulo_, modulo_);
}

pair<ll, ll> excrt(const vector<ll> &rem, const vector<ll> &mod) {
	int n = rem.size();
	ll r1 = rem[0], m1 = mod[0];
	for(int i = 1; i < n; ++i) {
		ll r2 = rem[i], m2 = mod[i];
		auto [g, p, _] = exgcd(m1, m2);
		if((r2 - r1) % g != 0) {
			return { -1, -1 };
		}
		ll v = m2 / g, x = (r2 - r1) / g;
		ll u = p % v * x % v;
		ll w = (u % v + v) % v;
		r1 += w * m1;
		m1 = lcm(m1, m2);
	}
	return { (r1 % m1 + m1) % m1, m1 };
}

整除分块

// 要计算\sum floor(n/i)
ll intdiv(ll n) {
	ll ans = 0;
	for(ll b = 1; b <= n;) {
		ll val = n / b;
		ll r = n / val;
		ll len = r - b + 1;
		ans = (ans + (len % modulo * val % modulo)) % modulo;
		b = r + 1;
	}
	return ans;
}

斯特林数

预处理

struct Sterling {
	Sterling() = delete;
	static int get(int n, int m) {
		return ster[n][m];
	}
private:
	static constexpr int maxn = 5005;
	static inline int ster[maxn][maxn];
	static inline int init = [&] {
		ster[0][0] = 1;
		for(ll i = 1; i < maxn; ++i) {
			ster[i][0] = 0;
			for(ll j = 1; j < i; ++j) {
				ster[i][j] = (ll(ster[i - 1][j - 1])
					+ ll(ster[i - 1][j]) * j) % modulo;
			}
		}
		return 0;
	}();
};

使用NTT求一行

得到的是 $ \displaystyle \left{0\atop k \right},\left{1\atop k \right},\dots,\left{k\atop k \right} $

vector<ll> sterling_ntt(int k) {
	if(k == 0) return {1};
	vector<ll> powk(k + 1, 0);
	powk[1] = 1;
	vector<bool> isprime(k + 1, true);
	isprime[0] = isprime[1] = false;
	vector<int> primes;
	for(int i = 2; i <= k; ++i) {
		if(isprime[i]) {
			primes.push_back(i);
			powk[i] = qpow(i, k);
		}
		for(ll p : primes) {
			ll q = i * p;
			if(q > k) break;
			isprime[q] = false;
			powk[q] = powk[i] * powk[p] % modulo;
			if(i % p == 0) break;
		}
	}
	vector<ll> fact(k + 1), invfact(k + 1);
	fact[0] = fact[1] = 1;
	for(ll i = 2; i <= k; ++i) {
		fact[i] = fact[i - 1] * i % modulo;
	}
	invfact[k] = qpow(fact[k], modulo - 2);
	for(int i = k - 1; i >= 0; --i) {
		invfact[i] = invfact[i + 1] * (i + 1) % modulo;
	}
	int n = 1;
	while(n < (2 * k + 1)) {
		n <<= 1;
	}
	vector<ll> a(n, 0), b(n, 0);
	for(int i = 0; i <= k; ++i) {
		a[i] = powk[i] * invfact[i] % modulo;
		if(i % 2 == 1) {
			b[i] = (modulo - invfact[i]) % modulo;
		} else {
			b[i] = invfact[i];
		}
	}
	auto ntt = [](auto &&ntt, vector<ll> &f, bool invert) -> void {
		int n = f.size();
		if(n == 1) return;
		vector<ll> f0(n / 2), f1(n / 2);
		for(int i = 0; i < n / 2; ++i) {
			f0[i] = f[2 * i];
			f1[i] = f[2 * i + 1];
		}
		ntt(ntt, f0, invert);
		ntt(ntt, f1, invert);
		ll w = 1, wn = qpow(g, (modulo - 1) / n);
		if(invert) {
			wn = qpow(wn, modulo - 2);
		}
		for(int i = 0; i < n / 2; ++i) {
			ll u = f0[i];
			ll v = w * f1[i] % modulo;
			f[i] = (u + v) % modulo;
			f[i + n / 2] = (u - v + modulo) % modulo;
			w = w * wn % modulo;
		}
	};
	ntt(ntt, a, false);
	ntt(ntt, b, false);
	vector<ll> c(n);
	for(int i = 0; i < n; ++i) {
		c[i] = a[i] * b[i] % modulo;
	}
	ntt(ntt, c, true);
	ll invn = qpow(n, modulo - 2);
	for(int i = 0; i < n; ++i) {
		c[i] = c[i] * invn % modulo;
	}
	c.resize(k + 1);
	return c;
}

质数筛和它能干的事

ll __qpow(ll x, ll n) {
	ll ret = 1;
	for(; n != 0; n >>= 1, x = x * x) {
		if(n & 1) ret = ret * x;
	}
	return ret;
}

template<class F> concept eularsieve_func = convertible_to<F, function<ll(int, int)>>;

struct EularSieve {
	vector<int> primes, lpf, lpow;
	vector<ll> fv;
	explicit EularSieve(int n) : lpf(n + 1), lpow(n + 1) {
		for(ll i = 2; i <= n; ++i) {
			if(lpf[i] == 0) {
				lpf[i] = i;
				lpow[i] = 1;
				primes.push_back(i);
			}
			for(ll p : primes) {
				ll j = i * p;
				if(j > n) break;
				lpf[j] = p;
				if(i % p == 0) {
					lpow[j] = lpow[i] + 1;
					ll jp = __qpow(lpf[j], lpow[j]);
					ll rem = j / jp;
					break;
				} else {
					lpow[j] = 1;
				}
			}
		}
	}
	template<eularsieve_func F> EularSieve(int n, F &&f) : lpf(n + 1), lpow(n + 1), fv(n + 1) {
		fv[1] = 1;
		for(ll i = 2; i <= n; ++i) {
			if(lpf[i] == 0) {
				lpf[i] = i;
				lpow[i] = 1;
				primes.push_back(i);
				fv[i] = f(i, 1);
			}
			for(ll p : primes) {
				ll j = i * p;
				if(j > n) break;
				lpf[j] = p;
				if(i % p == 0) {
					lpow[j] = lpow[i] + 1;
					ll jp = __qpow(lpf[j], lpow[j]);
					ll rem = j / jp;
					if(rem == 1) {
						fv[j] = f(p, lpow[j]);
					} else {
						fv[j] = fv[rem] * fv[jp];
					}
					break;
				} else {
					fv[j] = fv[i] * fv[p];
					lpow[j] = 1;
				}
			}
		}
	}
};

int eular_f(int p, int k) {
	return __qpow(p, k) - __qpow(p, k - 1);
}
int mobius_f(int p, int k) {
	return k == 0 ? 1 : k == 1 ? -1 : 0;
}
int factor_cnt_f(int p, int k) {
	return k + 1;
}
int factor_sum_f(int p, int k) {
	return (__qpow(p, k + 1) - 1ll) / ll(p - 1);
}

多项式乘法

FFT

using ld = long double;
using cd = complex<ld>;
constexpr ld pi = 3.14159265358979323846264338327950288l;
vector<ld> multiply(const vector<ld> &a, const vector<ld> &b) {
	int n = 1;
	while(n < (a.size() + b.size())) {
		n <<= 1;
	}
	vector<cd> fa(n), fb(n);
	for(int i = 0; i < a.size(); ++i) {
		fa[i] = a[i];
	}
	for(int i = 0; i < b.size(); ++i) {
		fb[i] = b[i];
	}
	auto fft = [](auto &&fft, vector<cd> &f, bool invert) -> void {
		int n = f.size();
		if(n == 1) return;
		vector<cd> f0(n / 2), f1(n / 2);
		for (int i = 0; i < n / 2; ++i) {
			f0[i] = f[2 * i];
			f1[i] = f[2 * i + 1];
		}
		fft(fft, f0, invert);
		fft(fft, f1, invert);
		ld theta = 2.l * pi / n * (invert ? -1.l : 1.l);
		cd wt = 1, w(cos(theta), sin(theta));
		for (int t = 0; t < n / 2; ++t) {
			cd u = f0[t], v = wt * f1[t];
			f[t] = u + v;
			f[t + n / 2] = u - v;
			wt *= w;
		}
	};
	fft(fft, fa, false);
	fft(fft, fb, false);
	for(int i = 0; i < n; ++i) {
		fa[i] *= fb[i];
	}
	fft(fft, fa, true);
	for(int i = 0; i < n; ++i) {
		fa[i] /= n;
	}
	vector<ld> ret(n);
	for(int i = 0; i < n; ++i) {
		ret[i] = fa[i].real();
	}
	while(ret.size() > (a.size() + b.size() - 1)) {
		ret.pop_back();
	}
	return ret;
}

NTT

constexpr ll modulo = 9'9824'4353, g = 3;
// constexpr ll modulo = 10'0000'0007, g = 5; 不可以
inline ll qpow(ll x, ll n) {
	ll ret = 1;
	for(; n != 0; n >>= 1, x = x * x % modulo) {
		if(n & 1) ret = ret * x % modulo;
	}
	return ret;
}
vector<ll> multiply(const vector<ll> &a, const vector<ll> &b) {
	int n = 1;
	while(n < a.size() + b.size()) n <<= 1;
	vector<ll> ca(n), cb(n);
	for(int i = 0; i < a.size(); ++i) ca[i] = a[i];
	for(int i = 0; i < b.size(); ++i) cb[i] = b[i];
	auto ntt = [](auto &&ntt, vector<ll> &f, bool invert) -> void {
		int n = f.size();
		if(n == 1) return;
		vector<ll> f0(n / 2), f1(n / 2);
		for(int i = 0; i < n / 2; ++i) {
			f0[i] = f[2 * i];
			f1[i] = f[2 * i + 1];
		}
		ntt(ntt, f0, invert);
		ntt(ntt, f1, invert);
		ll w = 1, wn = qpow(g, (modulo - 1) / n);
		if(invert) wn = qpow(wn, modulo - 2);
		for(int t = 0; t < n / 2; ++t) {
			ll u = f0[t], v = w * f1[t] % modulo;
			f[t] = (u + v) % modulo;
			f[t + n / 2] = (u - v + modulo) % modulo;
			w = w * wn % modulo;
		}
	};
	ntt(ntt, ca, false);
	ntt(ntt, cb, false);
	for(int i = 0; i < n; ++i) ca[i] = ca[i] * cb[i] % modulo;
	ntt(ntt, ca, true);
	for(int i = 0; i < n; ++i) ca[i] = ca[i] * qpow(n, modulo - 2) % modulo;
	while(ca.size() > (a.size() + b.size() - 1)) ca.pop_back();
	return ca;
}

FWT

constexpr ll modulo = 998244353, inv2 = 499122177;
void fwt_or(vector<ll> &a, bool invert) {
	ll n = a.size(), type = invert ? -1 : 1;
	for(ll x = 2; x <= n; x <<= 1) {
		ll k = x >> 1;
		for(ll i = 0; i < n; i += x) 
			for(ll j = 0; j < k; ++j) 
				a[i + j + k] = (a[i + j + k] + a[i + j] * type) % modulo;
	}
}
void fwt_and(vector<ll> &a, bool invert) {
	ll n = a.size(), type = invert ? -1 : 1;
	for(ll x = 2; x <= n; x <<= 1) {
		ll k = x >> 1;
		for(ll i = 0; i < n; i += x) 
			for(ll j = 0; j < k; ++j) 
				a[i + j] = (a[i + j] + a[i + j + k] * type) % modulo;
	}
}
void fwt_xor(vector<ll> &a, bool invert) {
	ll n = a.size(), type = invert ? inv2 : 1;
	for(ll x = 2; x <= n; x <<= 1) {
		ll k = x >> 1;
		for(ll i = 0; i < n; i += x) {
			for(ll j = 0; j < k; ++j) {
				ll u = a[i + j], v = a[i + j + k];
				a[i + j] = (u + v) % modulo;
				a[i + j + k] = (u - v) % modulo;
				a[i + j] = (a[i + j] * type) % modulo;
				a[i + j + k] = (a[i + j + k] * type) % modulo;
			}
		}
	}
}
void fwt_xnor(vector<ll> &a, bool invert) {
	reverse(a.begin(), a.end());
	fwt_xor(a, invert);
	reverse(a.begin(), a.end());
}
vector<ll> fwt_transform(const vector<ll> &a, const vector<ll> &b,
	void (*func)(vector<ll> &a, bool invert)) {
	ll n = 1;
	while(n < max(a.size(), b.size())) n <<= 1;
	vector<ll> A(n, 0), B(n, 0), C(n, 0);
	for(ll i = 0; i < a.size(); i++) A[i] = a[i];
	for(ll i = 0; i < b.size(); i++) B[i] = b[i];
	func(A, false);
	func(B, false);
	for(ll i = 0; i < n; i++) {
		C[i] = (A[i] * B[i]) % modulo;
	}
	func(C, true);
	for(ll i = 0; i < n; i++) {
		C[i] = (C[i] % modulo + modulo) % modulo;
	}
	return C;
}

线性基

struct LinearBasis_XOR {
	LinearBasis_XOR() : base(61) {}
	void insert(ll val) {
		for(int i = 60; i >= 0; --i) {
			if(((val >> i) & 1) == 0) continue;
			if(base[i] == 0) {
				base[i] = val;
				return;
			}
			val ^= base[i];
		}
	}
	ll query_max() const {
		ll ans = 0;
		for(int i = 60; i >= 0; --i) {
			if((ans ^ base[i]) > ans) {
				ans ^= base[i];
			}
		}
		return ans;
	}
private:
	vector<ll> base;
};

矩阵乘法和快速幂

template<class T> vector<vector<T>> matmul
	(const vector<vector<T>> &lhs, const vector<vector<T>> &rhs) {
	int M = lhs.size(), N = lhs[0].size(), P = rhs[0].size();
	vector<vector<T>> ret(M, vector<T>(P, 0));
	for(int i = 0; i < M; ++i) {
		for(int j = 0; j < P; ++j) {
			for(int k = 0; k < N; ++k) {
				ret[i][j] = ret[i][j] + lhs[i][k] * rhs[k][j];
			}
		}
	}
	return ret;
}
template<class T> vector<vector<T>> matpow(vector<vector<T>> mat, ll N) {
	int M = mat.size();
	vector<vector<T>> ret(M, vector<T>(M, 0));
	for(int i = 0; i < M; ++i) {
		ret[i][i] = static_cast<T>(1);
	}
	while(N) {
		if(N & 1ll) {
			ret = matmul(ret, mat);
		}
		mat = matmul(mat, mat);
		N >>= 1;
	}
	return ret;
}

高斯消元

vector<vector<ll>> gauss(const vector<vector<ll>> &a, const vector<vector<ll>> &b) {
	int r = a.size(), n = a[0].size(), m = b[0].size(), row = 0;
	vector<vector<ll>> aug(r, vector<ll>(n + m));
	for(int i = 0; i < r; ++i) {
		for(int j = 0; j < m + n; ++j) {
			aug[i][j] = (j < n) ? a[i][j] : b[i][j - n];
		}
	}
	vector<int> where(n, -1);
	for(int col = 0; col < n && row < r; ++col) {
		int sel = row;
		while(sel < r && aug[sel][col] == 0) ++sel;
		if(sel == r) continue;
		swap(aug[row], aug[sel]);
		ll inv = qpow(aug[row][col], modulo - 2);
		for(int j = col; j < n + m; ++j) {
			aug[row][j] = aug[row][j] * inv % modulo;
		}
		for(int i = 0; i < r; ++i) {
			if(i != row && aug[i][col]) {
				ll f = aug[i][col];
				for(int j = col; j < n + m; ++j) {
					aug[i][j] = (aug[i][j]
						- f * aug[row][j] % modulo + modulo) % modulo;
				}
			}
		}
		where[col] = row;
		++row;
	}
	for(int i = row; i < r; ++i) {
		bool flag = true;
		for(int j = 0; j < n; ++j) {
			if(aug[i][j] != 0) {
				flag = false;
				break;
			}
		}
		if(flag) {
			for(int j = 0; j < m; ++j) {
				if(aug[i][n + j]) throw runtime_error("No solution");
			}
		}
	}
	for(int col = 0; col < n; ++col) {
		if(where[col] == -1) throw runtime_error("Multiple solutions");
	}
	vector<vector<ll>> ret(n, vector<ll>(m, 0));
	for(int col = 0; col < n; ++col) {
		for(int j = 0; j < m; ++j) {
			ret[col][j] = aug[where[col]][n + j];
		}
	}
	return ret;
}

Lehmer码

l[i] = k代表后面有k个数比a[i]

// 均为0-based排列
vector<int> lehmer(const vector<int> &a) {
	int n = a.size();
	vector<int> l(n);
	Fenwick bit(n);
	for(int i = 1; i <= n; ++i) {
		bit.update(i, 1);
	}
	for(int i = 0; i < n; ++i) {
		int x = a[i] + 1;
		l[i] = bit.query(n) - bit.query(x);
		bit.update(x, -1);
	}
	return l;
}
vector<int> rev_lehmer(const vector<int> &l) {
	int n = l.size();
	VST vst(n); // 使用权值线段树实现会快一点
	for(int i = 0; i < n; ++i) {
		vst.insert(i);
	}
	vector<int> ret(n);
	for(int i = 0; i < n; ++i) {
		ret[i] = vst.qvr(l[i] + 1);
		vst.erase(ret[i]);
	}
	return ret;
}

计算几何

常量与数据类型

using ld = long double;
constexpr ld eps = 1e-9, pi = 3.1415926535897932384626l, inf = 1e12l;
int sign(ld a) {
	return (a < -eps) ? -1 : (a > eps) ? 1 : 0;
}
int cmp(ld a, ld b) {
	return sign(a - b);
}
strong_ordering cmpso(ld a, ld b) {
	return cmp(a, b) <=> 0;
}

点和向量

struct Point {
	ld x, y;
	Point() : x(0.0l), y(0.0l) {}
	Point(ld _x, ld _y) : x(_x), y(_y) {}
	Point(const complex<ld> &cd) : x(cd.real()), y(cd.imag()) {}
	operator complex<ld>() const {
		return complex<ld>(x, y);
	}
	Point operator+(const Point &p) const {
		return Point(x + p.x, y + p.y);
	}
	Point operator-(const Point &p) const {
		return Point(x - p.x, y - p.y);
	}
	Point operator*(ld z) const {
		return Point(z * x, z * y);
	}
	friend Point operator*(ld z, const Point &p) {
		return p * z;
	}
	Point operator/(ld z) const {
		return Point(x / z, y / z);
	}
	bool operator==(const Point &p) const {
		return cmp(x, p.x) == 0 && cmp(y, p.y) == 0;
	}
	ld len2() const {
		return x * x + y * y;
	}
	ld len() const {
		return sqrt(x * x + y * y);
	}
	// [0, 2*geo_pi)
	ld arg() const {
		ld ret = atan2(y, x);
		int c = cmp(ret, 0);
		return c == 1 ? ret : c == 0 ? 0.0l : ret + 2 * pi;
	}
	Point rotate(ld a) const {
		return Point(x * cos(a) - y * sin(a), x * sin(a) + y * cos(a));
	}
};
using Vector = Point;
ld dot(const Vector &x, const Vector &y) {
	return x.x * y.x + x.y * y.y;
}
ld cross(const Vector &x, const Vector &y) {
	return x.x * y.y - x.y * y.x;
}
ld cross(const Point &o, const Point &a, const Point &b) {
	return cross(a - o, b - o);
}
bool argcmp(const Point &x, const Point &y) {
	bool bx = sign(x.y) == 1 || (sign(x.y) == 0 && sign(x.x) == 1),
		 by = sign(y.y) == 1 || (sign(y.y) == 0 && sign(y.x) == 1);
	if(bx != by) return bx;
	return sign(cross(x, y)) == 0;
}
ld dist(const Point &x, const Point &y) {
	return (x - y).len();
}
int to_left(const Vector &a, const Vector &b) {
	return sign(cross(a, b));
}
int to_left(const Point &a, const Point &b, const Point &c) {
	return sign(cross(a, b, c));
}
ld angle(const Vector &a, const Vector &b) {
	ld cosa = clamp(dot(a, b) / (a.len() * b.len()), -1.0l, 1.0l);
	ld ret = acos(cosa);
	if(to_left(a, b) == -1) {
		ret = 2 * pi - ret;
	}
	return ret;
}

直线

struct Line {
	Point p, v;
	Line() {}
	Line(const Point &_p, const Vector &_v) : p(_p), v(_v) {}
	ld at(ld x) const {
		if(sign(v.x) == 0) return -inf;
		ld delx = x - p.x;
		ld ratio = delx / v.x;
		return (p + ratio * v).y;
	}
};
int to_left(const Line &ln, const Point &p) {
	return to_left(ln.v, p - ln.p);
}
bool parallel(const Line &l1, const Line &l2) {
	return sign(cross(l1.v, l2.v)) == 0;
}
int is_inter(const Line &l1, const Line &l2) {
	return parallel(l1, l2) ? 0 : 1;
}
Point inter(const Line &a, const Line &b) {
	if(parallel(a, b))
		throw runtime_error("a and b are parallel");
	Vector v = a.v * (cross(b.v, a.p - b.p) / cross(a.v, b.v));
	return a.p + v;
}
ld dist(const Point &p, const Line &ln) {
	return abs(cross(ln.v, p - ln.p)) / ln.v.len();
}
ld dist(const Line &l1, const Line &l2) {
	if(!parallel(l1, l2)) {
		return 0;
	}
	return dist(l1.p, l2);
}
Point proj(const Point &p, const Line &ln) {
	Vector v = ln.v * (dot(ln.v, p - ln.p) / (dot(ln.v, ln.v)));
	return ln.p + v;
}
bool is_on(const Point &p, const Line &ln) {
	return sign(cross(ln.v, ln.p - p)) == 0;
}
Line midperp(const Point &a, const Point &b) {
	Point mid = (a + b) / 2;
	Vector to(-(b - a).y, (b - a).x);
	return Line(mid, to);
}

线段

struct Lineseg {
	Point a, b;
	Lineseg() {}
	Lineseg(const Point &_a, const Point &_b) : a(_a), b(_b) {}
	ld len() const {
		return (b - a).len();
	}
	ld at(ld x) const {
		if(cmp(x, min(a.x, b.x)) == -1 || cmp(x, max(a.x, b.x)) == 1) return -inf;
		if(cmp(a.x, b.x) == 0) {
			if(cmp(a.x, x) != 0) return -inf;
			return max(a.y, b.y);
		}
		Line l(a, b - a);
		return l.at(x);
	}
};
int is_on(const Point &p, const Lineseg &ls) {
	if(p == ls.a || p == ls.b) return 2;
	return to_left(p - ls.a, p - ls.b) == 0 && sign(dot(p - ls.a, p - ls.b)) == -1;
}
int is_inter(const Line &ln, const Lineseg &ls) {
	int a = to_left(ln, ls.a), b = to_left(ln, ls.b);
	if(a == 0 || b == 0) return 2;
	return a == b ? 0 : 1;
}
int is_inter(const Lineseg &l1, const Lineseg &l2) {
	if(is_on(l1.a, l2) || is_on(l1.b, l2) || is_on(l2.a, l1) || is_on(l2.b, l1)) 
		return 2;
	Line ln1(l1.a, l1.b - l1.a), ln2(l2.a, l2.b - l2.a);
	return to_left(ln1, l2.a) * to_left(ln1, l2.b) == -1 
		&& to_left(ln2, l1.a) * to_left(ln2, l1.b) == -1;
}
ld dist(const Point &p, const Lineseg &ls) {
	if(is_on(p, ls) != 0) return 0.0l;
	if(sign(dot(p - ls.a, ls.b - ls.a)) == -1 ||
	   sign(dot(p - ls.b, ls.a - ls.b)) == -1) 
		return min(dist(p, ls.a), dist(p, ls.b));
	Line l(ls.a, ls.b - ls.a);
	return dist(p, l);
}

多边形

struct Polygon {
	vector<Point> pts;
	Polygon() {}
	Polygon(const vector<Point> &p) : pts(p) {}
	Polygon(const vector<Line> &l) {
		pts.reserve(l.size());
		for(int i = 0; i < l.size(); ++i) {
			pts.emplace_back(inter(l[i], l[(i + 1) % l.size()]));
		}
	}
	ld area() const {
		ld ret = 0.0l;
		for(int i = 0; i < pts.size(); ++i) {
			ret += cross(pts[i], pts[(i + 1) % pts.size()]);
		}
		return ret / 2.0l;
	}
	ld circ() const {
		ld ret = 0.0l;
		for(int i = 0; i < pts.size(); ++i) {
			ret += (pts[i] - pts[(i + 1) % pts.size()]).len();
		}
		return ret;
	}
	int size() const {
		return pts.size();
	}
};

struct Circle {
	Point c;
	ld r;
	Circle() : r(0.0l) {}
	Circle(const Point &_c, ld _r) : c(_c), r(_r) {}
	ld area() const {
		return pi * r * r;
	}
	ld circ() const {
		return 2.0l * pi * r;
	}
};
bool is_inter(const Circle &c1, const Circle &c2) {
	ld dis = (c2.c - c1.c).len();
	ld r1 = c1.r, r2 = c2.r;
	if(abs(dis) <= eps) {
		return false;
	}
	if(dis > r1 + r2 + eps || dis < abs(r1 - r2) - eps) {
		return false;
	}
	return true;
}
pair<Point, Point> inter(const Circle &c1, const Circle &c2) {
	if(!is_inter(c1, c2))
		throw runtime_error("Circles do not intersect");
	ld dis = (c2.c - c1.c).len();
	ld r1 = c1.r, r2 = c2.r;
	ld cosa = clamp((r1 * r1 + dis * dis - r2 * r2) / (2 * r1 * dis), -1.0l, 1.0l);
	ld alp = acos(cosa);
	Point v = c2.c - c1.c;
	v = v / v.len() * c1.r;
	return { c1.c + v.rotate(alp), c1.c + v.rotate(-alp) };
}
bool is_inter(const Circle &c, const Line &l) {
	ld d = dist(c.c, l);
	return cmp(c.r, d) != -1;
}
pair<Point, Point> inter(const Circle &c, const Line &l) {
	if(!is_inter(c, l))
		throw runtime_error("Circle and line do not intersect");
	Point o = c.c;
	Vector v = l.v;
	Point p = l.p - o;
	ld A = v.len2(), B = 2 * dot(p, v), C = p.len2() - c.r * c.r,
		D = max(B * B - 4 * A * C, 0.0l);
	ld s = sqrt(D);
	ld q = (B > 0 ? -B - s : -B + s) / 2;
	ld t1 = q / A, t2 = C / q;
	Point i1 = l.p + v * t1, i2 = l.p + v * t2;
	return {i1, i2};
}

平面凸包

vector<Point> andrew(vector<Point> points) {
	sort(points.begin(), points.end(), [](const Point &a, const Point &b) {
		return a.x < b.x || (a.x == b.x && a.y < b.y);
	});
	points.erase(unique(points.begin(), points.end()), points.end());
	int n = points.size();
	if(n <= 2) {
		return points;
	}
	vector<Point> stk;
	for(int i = 0; i < n; ++i) {
		while(stk.size() >= 2 &&
			sign(cross(stk[stk.size() - 2], stk.back(), points[i])) <= 0) {
			stk.pop_back();
		}
		stk.push_back(points[i]);
	}
	int t = stk.size();
	for(int i = n - 2; i >= 0; --i) {
		while(stk.size() > t &&
			sign(cross(stk[stk.size() - 2], stk.back(), points[i])) <= 0) {
			stk.pop_back();
		}
		stk.push_back(points[i]);
	}
	stk.pop_back();
	return stk;
}

凸多边形

struct Convex : public Polygon {
	using Polygon::Polygon;
};
bool is_in(const Point &pt, const Convex &convex) {
	if(convex.size() == 1) {
		return pt == convex.pts[0];
	}
	if(convex.size() == 2) {
		return is_on(pt, Lineseg(convex.pts[0], convex.pts[1]));
	}
	auto check = [](const Point &a, const Point &b, const Point &c, const Point &p) {
		ld c1 = cross(b - a, p - a), c2 = cross(c - b, p - b),
			c3 = cross(a - c, p - c);
		return (sign(c1) != -1 && sign(c2) != -1 && sign(c3) != -1) 
			|| (sign(c1) != 1 && sign(c2) != 1 && sign(c3) != 1);
	};
	int n = convex.size();
	Point pivot = convex.pts[0];
	if((sign(cross(convex.pts[1] - pivot, pt - pivot)) == -1) 
	|| (sign(cross(convex.pts[n - 1] - pivot, pt - pivot)) == 1)) {
		return false;
	}
	int l = 1, r = n - 1;
	while(l + 1 < r) {
		int mid = (l + r) >> 1;
		if(sign(cross(convex.pts[mid] - pivot, pt - pivot)) != -1) {
			l = mid;
		} else {
			r = mid;
		}
	}
	int nxt = (l == n - 1) ? 1 : l + 1;
	return check(pivot, convex.pts[l], convex.pts[nxt], pt);
}
pair<Point, Point> tangent(const Point &pt, const Convex &convex) {
	if(is_in(pt, convex)) throw runtime_error("pt is in the convex");
	int n = convex.size();
	auto peak = [&](int l, int r, bool find_r) {
		while(l < r - 1) {
			int mid = (l + r) / 2;
			if(find_r ^ (to_left(convex.pts[(mid + n - 1) % n] - pt,
				convex.pts[mid] - pt) == 1)) {
				r = mid;
			} else {
				l = mid;
			}
		}
		return l;
	};
	if(to_left(convex.pts[0] - pt, convex.pts[1] - pt) == 0) {
		int idx = peak(2, n, cmp(dist(convex.pts[0], pt),
			dist(convex.pts[1], pt)) == -1);
		return { convex.pts[0], convex.pts[idx] };
	}
	bool all_left = true, all_right = true;
	auto chk = [&](int x) {
		if(x == 1) all_right = false;
		if(x == -1) all_left = false;
	};
	chk(to_left(convex.pts[0] - pt, convex.pts[1] - pt));
	chk(to_left(convex.pts[0] - pt, convex.pts[n - 1] - pt));
	if(all_left || all_right) {
		int idx = peak(1, n, all_left);
		return { convex.pts[0], convex.pts[idx] };
	}
	int l = 1, r = n;
	while(l < r - 1) {
		int mid = (l + r) / 2;
		if(to_left(convex.pts[0] - pt, convex.pts[mid] - pt) == 1) {
			l = mid;
		} else {
			r = mid;
		}
	}
	int split = l;
	bool flag = (to_left(convex.pts[0] - pt, convex.pts[1] - pt) == 1);
	int i1 = peak(0, split + 1, flag), i2 = peak(split, n, !flag);
	return { convex.pts[i1], convex.pts[i2] };
}
pair<Point, Point> tangent(const Line &ln, const Convex &convex) {
	int n = convex.size();
	Vector dir(-ln.v.y, ln.v.x);
	auto find = [&](bool flag) {
		int l = 0, r = n - 1;
		while(r - l > 2) {
			int m1 = l + (r - l) / 3, m2 = r - (r - l) / 3;
			ld dot1 = dot(convex.pts[m1], dir), dot2 = dot(convex.pts[m2], dir);
			if(flag ^ (cmp(dot1, dot2) == -1)) {
				r = m2;
			} else {
				l = m1;
			}
		}
		int idx = l;
		ld ret = dot(convex.pts[l], dir);
		for(int i = l; i <= r; ++i) {
			ld now = dot(convex.pts[i], dir);
			if(flag ^ (cmp(now, ret) == -1)) {
				idx = i;
				ret = now;
			}
		}
		return idx;
	};
	int i1 = find(true), i2 = find(false);
	return { convex.pts[i1], convex.pts[i2] };
}

半平面交

vector<Line> half_inter(vector<Line> lines) {
	lines.push_back({{-inf, 0.0l}, {0.0l, -1.0l}});
	lines.push_back({{inf, 0.0l}, {0.0l, 1.0l}});
	lines.push_back({{0.0l, inf}, {-1.0l, 0.0l}});
	lines.push_back({{0.0l, -inf}, {1.0l, 0.0l}});
	sort(lines.begin(), lines.end(), [](const Line &a, const Line &b) {
		int c = cmp(a.v.arg(), b.v.arg());
		if(c != 0) return c == -1;
		return sign(cross(a.v, b.p - a.p)) < 0;
	});
	deque<Line> dq;
	auto bad = [](const Line &l, const Line &b, const Line &c) {
		try {
			auto p = inter(b, c);
			return to_left(l, p) < 0;
		} catch(const invalid_argument &) {
			return false;
		}
	};
	for(auto &l : lines) {
		if(!dq.empty() && cmp(l.v.arg(), dq.back().v.arg()) == 0) {
			if(to_left(l, dq.back().p + dq.back().v) < 0) dq.back() = l;
			continue;
		}
		while(dq.size() > 1 && bad(l, dq.back(), dq[dq.size() - 2])) dq.pop_back();
		while(dq.size() > 1 && bad(l, dq[0], dq[1])) dq.pop_front();
		dq.push_back(l);
	}
	while(dq.size() > 1 && bad(dq[0], dq.back(), dq[dq.size() - 2])) dq.pop_back();
	while(dq.size() > 1 && bad(dq.back(), dq[0], dq[1])) dq.pop_front();
	vector<Line> ret(dq.begin(), dq.end());
	int m = ret.size();
	if(m < 3) return {};
	vector<Point> poly;
	poly.reserve(m);
	for(int i = 0; i < m; ++i) {
		try {
			poly.emplace_back(inter(ret[i], ret[(i + 1) % m]));
		} catch(const invalid_argument &) {
			return {};
		}
	}
	if(poly.size() != m) return {};
	return ret;
}

扫描线

struct Rectangle {
	Point p1, p2;
};

struct Event {
	int delta, l, r;
	ld y;
};

struct SegTree {
	SegTree(const vector<ld> &_xs) : nodes(4 * _xs.size()), xs(_xs) {
		int n = xs.size();
		_build(0, 0, n);
	}
	ld query() const {
		return nodes[0].len;
	}
	// 注意左闭右开
	void update(int l, int r, int v) {
		_update(0, l, r, v);
	}
private:
	struct Node {
		int l, r, cnt;
		ld len;
	};
	vector<Node> nodes;
	vector<ld> xs;
	void _pushup(int u) {
		if(nodes[u].cnt > 0) {
			nodes[u].len = xs[nodes[u].r] - xs[nodes[u].l];
		} else if(nodes[u].r - nodes[u].l == 1) {
			nodes[u].len = 0.0l;
		} else {
			int lson = (u << 1) + 1, rson = (u << 1) + 2;
			nodes[u].len = nodes[lson].len + nodes[rson].len;
		}
	}
	void _build(int u, int l, int r) {
		nodes[u] = { l, r, 0, 0.0l };
		if(r - l > 1) {
			int mid = (l + r) >> 1, lson = (u << 1) + 1, rson = (u << 1) + 2;
			_build(lson, l, mid);
			_build(rson, mid, r);
		}
	}
	void _update(int u, int l, int r, int v) {
		if(nodes[u].l >= r || nodes[u].r <= l) return;
		if(nodes[u].l >= l && nodes[u].r <= r) {
			nodes[u].cnt += v;
			_pushup(u);
			return;
		}
		int lson = (u << 1) + 1, rson = (u << 1) + 2;
		_update(lson, l, r, v);
		_update(rson, l, r, v);
		_pushup(u);
	}
};

ld scanline(const vector<Rectangle> &rects) {
	int n = rects.size();
	vector<ld> xs(2 * n);
	vector<Event> events(2 * n);
	for(int i = 0; i < n; ++i) {
		xs[2 * i] = rects[i].p1.x;
		xs[2 * i + 1] = rects[i].p2.x;
	}
	sort(xs.begin(), xs.end());
	xs.erase(unique(xs.begin(), xs.end()), xs.end());
	auto getid = [&](ld x) -> int {
		return lower_bound(xs.begin(), xs.end(), x) - xs.begin();
	};
	for(int i = 0; i < n; ++i) {
		events[2 * i] =
			{ 1, getid(rects[i].p1.x), getid(rects[i].p2.x), rects[i].p1.y };
		events[2 * i + 1] =
			{ -1, getid(rects[i].p1.x), getid(rects[i].p2.x), rects[i].p2.y };
	}
	sort(events.begin(), events.end(), [](const Event &a, const Event &b) {
		return cmp(a.y, b.y) == -1;
	});
	SegTree seg(xs);
	ld lasty = events[0].y;
	ld ans = 0.0l;
	for(auto &e : events) {
		ans += seg.query() * (e.y - lasty);
		seg.update(e.l, e.r, e.delta);
		lasty = e.y;
	}
	return ans;
}

旋转卡壳

ld farthest_dist(const vector<Point> &pts) {
	int n = pts.size();
	auto hull = andrew(pts);
	int m = hull.size();
	int j = 1;
	ld ans = 0;
	for(int i = 0; i < m; ++i) {
		int u = (i + 1) % m;
		while(cross(hull[i], hull[u], hull[(j + 1) % m])
			> cross(hull[i], hull[u], hull[j])) {
			j = (j + 1) % m;
		}
		ans = max({ ans, (hull[i] - hull[j]).len2(), (hull[u] - hull[j]).len2() });
	}
	return ans;
}

Pick定理

ll points_inside(const Polygon &poly) {
	ll twos = poly.twice_area();
	ll border = 0;
	for(int i = 0; i < poly.size(); ++i) {
		int j = (i + 1) % poly.size();
		Vector v = poly.pts[j] - poly.pts[i];
		border += gcd(abs(v.x), abs(v.y));
	}
	return (twos - border + 2) / 2;
}

阿波罗尼斯圆

// PA / PB = lambda
Circle apollonius(const Point &a, const Point &b, ld lambda) {
	if(cmp(lambda, 1) == 0) throw runtime_error("lambda is 1");
	if(lambda > 1) return apollonius(b, a, 1.0l / lambda);
	Vector v = b - a;
	Point p1 = a + v / (1 + lambda) * lambda, p2 = a - v / (1 - lambda) * lambda;
	return Circle((p1 + p2) / 2, dist(p1, p2) / 2);
}

圆的反演

// (A invert to C)'s shape, true is circle, false is line
bool inv_shape(const Circle &c, const Circle &a) {
	return cmp(dist(c.c, a.c), a.r) != 0;
}
bool inv_shape(const Circle &c, const Line &l) {
	return sign(cross(c.c - l.p, l.v)) != 0;
}
Point inverse(const Circle &c, const Point &a) {
	Vector v = a - c.c;
	return c.c + v * (c.r * c.r / v.len());
}
Circle invcircle(const Circle &c, const Circle &a) {
	if(!inv_shape(c, a)) throw runtime_error("result type mismatch");
	Vector v = a.c - c.c;
	Point p1 = a.c - v / v.len() * a.r, p2 = a.c + v / v.len() * a.r;
	Point p3 = inverse(c, p1), p4 = inverse(c, p2);
	return Circle((p3 + p4) / 2, dist(p3, p4) / 2);
}
Line invline(const Circle &c, const Circle &a) {
	if(inv_shape(c, a)) throw runtime_error("result type mismatch");
	Vector v = a.c - c.c;
	Point pt = c.c + v * 2;
	Point ip = inverse(c, pt);
	return Line(ip, Vector(-v.y, v.x));
}
Circle invcircle(const Circle &c, const Line &l) {
	if(!inv_shape(c, l)) throw runtime_error("result type mismatch");
	Point p1 = l.p, p2 = l.p + l.v, p3 = l.p - l.v;
	Point q1 = inverse(c, p1), q2 = inverse(c, p2), q3 = inverse(c, p3);
	ld x1 = q1.x, y1 = q1.y;
	ld x2 = q2.x, y2 = q2.y;
	ld x3 = q3.x, y3 = q3.y;
	ld A = x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2);
	ld S1 = q1.len2(), S2 = q2.len2(), S3 = q3.len2();
	ld ux = (S1 * (y2 - y3) + S2 * (y3 - y1) + S3 * (y1 - y2)) / (2 * A);
	ld uy = (S1 * (x3 - x2) + S2 * (x1 - x3) + S3 * (x2 - x1)) / (2 * A);
	Point o(ux, uy);
	ld r = dist(o, q1);
	return Circle(o, r);
}
Line invline(const Circle &c, const Line &l) {
	if(inv_shape(c, l)) throw runtime_error("result type mismatch");
	return l;
}

最小圆覆盖

Circle cover(const Point &a) {
	return Circle(a, 0);
}
Circle cover(const Point &a, const Point &b) {
	return Circle((a + b) / 2, (b - a).len() / 2);
}
Circle cover(const Point &a, const Point &b, const Point &c) {
	Line u = midperp(a, b), v = midperp(a, c);
	if(!is_inter(u, v)) throw runtime_error("three points are on a same line!");
	Point center = inter(u, v);
	return Circle(center, dist(center, a));
}
Circle cover(vector<Point> pts) {
	static mt19937 rng((unsigned)chrono::steady_clock::now().time_since_epoch().count());
	shuffle(pts.begin(), pts.end(), rng);
	if(pts.size() == 1) return cover(pts[0]);
	if(pts.size() == 2) return cover(pts[0], pts[1]);
	if(pts.size() == 3) return cover(pts[0], pts[1], pts[2]);
	int n = pts.size();
	Circle ret = cover(pts[0]);
	for(int i = 1; i < n; ++i) {
		if(ret.r < dist(ret.c, pts[i])) {
			ret = cover(pts[i]);
			for(int j = 0; j < i; ++j) {
				if(ret.r < dist(ret.c, pts[j])) {
					ret = cover(pts[i], pts[j]);
					for(int k = 0; k < j; ++k) {
						if(ret.r < dist(ret.c, pts[k])) {
							ret = cover(pts[i], pts[j], pts[k]);
						}
					}
				}
			}
		}
	}
	return ret;
}

李超线段树

struct LiChao {
	vector<Lineseg> lines;
	explicit LiChao(int ma) : id(ma * 4, -1), n(ma) {}
	void addline(int x1, int x2, int y1, int y2) {
		Lineseg ls({ld(x1), ld(y1)}, {ld(x2), ld(y2)});
		int i = lines.size();
		lines.push_back(ls);
		_update(i, min(x1, x2), max(x1, x2), 0, 0, n - 1);
	}
	int query(int k) const {
		return _query(k, 0, 0, n - 1).second;
	}
private:
	vector<int> id;
	int n;
	static pair<ld, int> _max(const pair<ld, int> &a, const pair<ld, int> &b) {
		int cmp1 = cmp(a.first, b.first);
		if(cmp1 != 0) return (cmp1 == 1) ? a : b;
		return a.second < b.second ? a : b;
	}
	void _update(int i, int ul, int ur, int u, int rl, int rr) {
		int mid = (rl + rr) / 2, ls = u * 2 + 1, rs = u * 2 + 2;
		if(ul <= rl && ur >= rr) {
			if(id[u] == -1) {
				id[u] = i;
				return;
			}
			if(cmp(lines[i].at(mid), lines[id[u]].at(mid)) == 1) swap(i, id[u]);
			if(cmp(lines[i].at(rl), lines[id[u]].at(rl)) == 1)
				_update(i, ul, ur, ls, rl, mid);
			if(cmp(lines[i].at(rr), lines[id[u]].at(rr)) == 1)
				_update(i, ul, ur, rs, mid + 1, rr);
		} else {
			if(ul <= mid) _update(i, ul, ur, ls, rl, mid);
			if(mid < ur) _update(i, ul, ur, rs, mid + 1, rr);
		}
	}
	pair<ld, int> _query(int k, int u, int rl, int rr) const {
		pair<ld, int> ret(-inf, -1);
		if(id[u] != -1) ret = {lines[id[u]].at(k), id[u]};
		if(rl == rr) return ret;
		int mid = (rl + rr) / 2, ls = u * 2 + 1, rs = u * 2 + 2;
		if(k <= mid) ret = _max(ret, _query(k, ls, rl, mid));
		else ret = _max(ret, _query(k, rs, mid + 1, rr));
		return ret;
	}
};

三维计算几何

constexpr ld eps = 2e-10l, pi = 3.14159265358979323846264338327950288l;
int sign(ld x) {
	return x > eps ? 1 : x < -eps ? -1 : 0;
}
int cmp(ld x, ld y) {
	return sign(x - y);
}

struct Point {
	ld x, y, z;
	Point() : x(0), y(0), z(0) {}
	Point(ld _x, ld _y, ld _z) : x(_x), y(_y), z(_z) {}
	Point operator+(const Point &p) const { return Point(x + p.x, y + p.y, z + p.z); }
	Point operator-(const Point &p) const { return Point(x - p.x, y - p.y, z - p.z); }
	Point operator*(ld u) const { return Point(x * u, y * u, z * u); }
	friend Point operator*(ld u, const Point &p) { return p * u; }
	Point operator/(ld u) const { return Point(x / u, y / u, z / u); }
	ld len() const { return sqrt(x * x + y * y + z * z); }
	ld len2() const { return x * x + y * y + z * z; }
	bool operator==(const Point &p) const {
		return cmp(x, p.x) == 0 && cmp(y, p.y) == 0 && cmp(z, p.z) == 0;
	}
	bool operator>(const Point &p) const {
		return (cmp(x, p.x) != 0) ? (x > p.x) :
			(cmp(y, p.y) != 0) ? (y > p.y) : (cmp(z, p.z) == 1);
	}
	bool operator<(const Point &p) const {
		return (cmp(x, p.x) != 0) ? (x < p.x) :
			(cmp(y, p.y) != 0) ? (y < p.y) : (cmp(z, p.z) == -1);
	}
};
using Vector = Point;
ld dot(const Vector &a, const Vector &b) {
	return a.x * b.x + a.y * b.y + a.z * b.z;
}
Vector cross(const Vector &a, const Vector &b) {
	return Vector(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}
Vector cross(const Point &o, const Point &a, const Point &b) {
	return cross(a - o, b - o);
}
ld mix_prod(const Vector &a, const Vector &b, const Vector &c) {
	return dot(cross(a, b), c);
}
ld dist(const Point &a, const Point &b) {
	return (a - b).len();
}
bool same_plane(const Point &a, const Point &b, const Point &c, const Point &d) {
	return sign(mix_prod(b - a, c - a, d - a)) == 0;
}
bool parallel(const Vector &a, const Vector &b) {
	Vector v = cross(a, b);
	return sign(v.len()) == 0;
}
ld operator/(const Vector &a, const Vector &b) {
	if(!parallel(a, b)) throw runtime_error("a and b don't parallel");
	return sign(b.x) == 0 ? a.y / b.y : a.x / b.x;
}
Point proj(const Point &p, const Vector &base) {
	if(sign(base.len2()) == 0) return Point(0, 0, 0);
	ld t = dot(p, base) / base.len2();
	return base * t;
}

struct Line {
	Point p, v;
	Line() {}
	Line(const Point &_p, const Vector &_v) : p(_p), v(_v) {}
};
bool parallel(const Line &a, const Line &b) {
	return parallel(a.v, b.v);
}
bool same_plane(const Line &a, const Line &b) {
	return same_plane(a.p, a.p + a.v, b.p, b.p + b.v);
}
bool is_inter(const Line &a, const Line &b) {
	return same_plane(a, b) && !parallel(a, b);
}
Point inter(const Line &a, const Line &b) {
	if(!is_inter(a, b)) throw runtime_error("a and b don't intersect");
	Vector w = b.p - a.p, v1v2 = cross(a.v, b.v);
	Vector wv2 = cross(w, b.v);
	ld den = v1v2.len2();
	ld t = dot(wv2, v1v2) / den;
	return a.p + a.v * t;
}

struct Plane {
	Point p, norm;
	Plane() {}
	Plane(const Point &_p, const Vector &_n) : p(_p), norm(_n) {}
	Plane(const Point &a, const Point &b, const Point &c)
		: p(a), norm(cross(b - a, c - a)) {}
};
bool parallel(const Plane &a, const Plane &b) {
	return parallel(a.norm, b.norm);
}

字符串

KMP

// 得到用于KMP的数组
vector<int> kmp_f(const string &s) {
	int n = s.size();
	vector<int> f(n, 0);
	for(int i = 1, j = 0; i < n; ++i) {
		while(j > 0 && s[i] != s[j]) j = f[j - 1];
		if(s[i] == s[j]) ++j;
		f[i] = j;
	}
	return f;
}
// 寻找第一次在off后面haystack中的needle
int kmp_find(const string &haystack, const string &needle, int off = 0) {
	int n = needle.size(), m = haystack.size();
	vector<int> f = kmp_f(needle);
	for(int i = 0, j = off; j < m;) {
		if(haystack[j] == needle[i]) {
			++i;
			++j;
			if(i == n) return j - n;
		} else {
			(i == 0) ? (++j) : (i = f[i - 1]);
		}
	}
	return -1;
}
// 返回haystack中有多少个needle,可重(即10101有两个101)
int kmp_count(const string &haystack, const string &needle) {
	int n = needle.size(), m = haystack.size();
	int ret = 0;
	vector<int> f = kmp_f(needle);
	for(int i = 0, j = 0; j < m;) {
		if(haystack[j] == needle[i]) {
			++i;
			++j;
			if(i == n) {
				++ret;
				i = f[i - 1];
			}
		} else {
			(i == 0) ? (++j) : (i = f[i - 1]);
		}
	}
	return ret;
}

Z函数(扩展KMP)

vector<int> zfn(const string &s) {
	int n = s.size();
	vector<int> z(n);
	for(int i = 1, l = 0, r = 0; i < n; ++i) {
		if(i <= r && z[i - l] < r - i + 1) {
			z[i] = z[i - l];
		} else {
			z[i] = max(0, r - i + 1);
			while(i + z[i] < n && s[z[i]] == s[i + z[i]]) {
				++z[i];
			}
		}
		if(i + z[i] - 1 > r) {
			l = i;
			r = i + z[i] - 1;
		}
	}
	return z;
}

字典树

struct Trie {
	Trie() : nodes(1) {}
	void insert(const string &str) {
		int rt = 0;
		for(int i = 0; i < str.size(); ++i) {
			if((nodes[rt].nxt[getnum(str[i])]) == -1) {
				nodes[rt].nxt[getnum(str[i])] = nodes.size();
				nodes.emplace_back();
			}
			nodes[rt].cnt++;
			rt = nodes[rt].nxt[getnum(str[i])];
		}
		nodes[rt].cnt++;
		nodes[rt].end = true;
	}
	bool find(const string &str) const {
		int rt = 0;
		for(int i = 0; i < str.size(); ++i) {
			if((nodes[rt].nxt[getnum(str[i])]) == -1) {
				return false;
			}
			rt = nodes[rt].nxt[getnum(str[i])];
		}
		return nodes[rt].end;
	}
	int find_prefix(const string &str) const {
		int rt = 0;
		for(int i = 0; i < str.size(); ++i) {
			if((nodes[rt].nxt[getnum(str[i])]) == -1) {
				return 0;
			}
			rt = nodes[rt].nxt[getnum(str[i])];
		}
		return nodes[rt].cnt;
	}
private:
	struct Node {
		array<int, 65> nxt;
		bool end;
		int cnt;
		Node() : end(false), cnt(0) {
			nxt.fill(-1);
		}
	};
	vector<Node> nodes;
	static int getnum(char x) {
		if(x >= 'A' && x <= 'Z') {
			return x - 'A';
		} else if(x >= 'a' && x <= 'z') {
			return x - 'a' + 26;
		} else {
			return x - '0' + 52;
		}
	}
};

Manacher

string manacher(const string &_s) {
	string s = "$";
	for(char ch : _s) {
		s += ch;
		s += '$';
	}
	int n = s.size();
	int max_right = 0;
	int center = 0;
	vector<int> dp(n, 0);
	auto expand = [&](int left, int right) {
		while(left >= 0 && right < n && s[left] == s[right]) {
			--left;
			++right;
		}
		return (right - left) / 2;
	};
	for(int i = 0; i < n; ++i) {
		int mirror = 2 * center - i;
		if(i >= max_right) {
			dp[i] = expand(i, i);
			max_right = i + dp[i];
			center = i;
		} else if(dp[mirror] == max_right - i) {
			dp[i] = expand(i - dp[mirror], i + dp[mirror]);
			max_right = i + dp[i];
			center = i;
		} else {
			dp[i] = min(dp[mirror], max_right - i);
		}
	}
	auto it = max_element(dp.begin(), dp.end());
	int id = it - dp.begin(), len = *it;
	string ret;
	for(int i = id - len + 1; i < id + len; ++i) {
		if(s[i] != '$') {
			ret += s[i];
		}
	}
	return ret;
}

AC自动机

千万不要忘了build!

struct ACAM {
	ACAM() : nodes(1) {}
	void insert(const string &str) {
		int now = 0;
		for(char ch : str) {
			int id = ch - 'a';
			int v = nodes[now].nxt1[id];
			if(v == -1) {
				v = nodes[now].nxt1[id] = nodes[now].nxt2[id] = nodes.size();
				nodes.emplace_back();
			}
			now = v;
		}
		nodes[now].cnt++;
	}
	void build() {
		order.clear();
		queue<int> q;
		nodes[0].fail = 0;
		for(int i = 0; i < 26; ++i) {
			int v = nodes[0].nxt2[i];
			if(v == -1) {
				nodes[0].nxt2[i] = 0;
			} else {
				nodes[v].fail = 0;
				q.push(v);
			}
		}
		while(!q.empty()) {
			int now = q.front();
			q.pop();
			order.push_back(now);
			for(int i = 0; i < 26; ++i) {
				int v = nodes[now].nxt2[i];
				if(v == -1) {
					nodes[now].nxt2[i] = nodes[nodes[now].fail].nxt2[i];
				} else {
					nodes[v].fail = nodes[nodes[now].fail].nxt2[i];
					q.push(v);
				}
			}
		}
	}
	int find(const string &s) const {
		int now = 0;
		for(char ch : s) {
			int v = nodes[now].nxt1[ch - 'a'];
			if(v == -1) {
				return -1;
			}
			now = v;
		}
		return now;
	}
	vector<int> frequency(const string &str) const {
		vector<int> ret(nodes.size(), 0);
		int now = 0;
		for(char ch : str) {
			int id = ch - 'a';
			now = nodes[now].nxt2[id];
			ret[now]++;
		}
		for(int i = order.size() - 1; i >= 0; --i) {
			int u = order[i];
			if(u != 0) {
				ret[nodes[u].fail] += ret[u];
			}
		}
		return ret;
	}
	int size() const {
		return nodes.size();
	}
private:
	struct Node {
		int cnt;
		int fail;
		array<int, 26> nxt1, nxt2;
		Node() : cnt(0), fail(0) {
			nxt1.fill(-1);
			nxt2.fill(-1);
		}
	};
	vector<Node> nodes;
	vector<int> order;
};

后缀数组

vector<int> suffix_array(const string &str) {
	int n = str.size(), m = 128, p = 0;
	vector<int> rk(n * 3, -1), sa(n), id(n), cnt(max(n, m), 0);
	for(int i = 0; i < n; ++i) {
		rk[i] = str[i];
		cnt[rk[i]]++;
	}
	for(int i = 1; i < m; ++i) {
		cnt[i] += cnt[i - 1];
	}
	for(int i = n - 1; i >= 0; --i) {
		cnt[rk[i]]--;
		sa[cnt[rk[i]]] = i;
	}
	for(int w = 1; ; w <<= 1, m = p + 1) {
		int cur = 0;
		for(int i = n - w; i < n; ++i) {
			id[cur] = i;
			++cur;
		}
		for(int i = 0; i < n; ++i) {
			if(sa[i] >= w) {
				id[cur] = sa[i] - w;
				++cur;
			}
		}
		cnt.assign(max(n, m), 0);
		for(int i = 0; i < n; ++i) {
			cnt[rk[i]]++;
		}
		for(int i = 1; i < m; ++i) {
			cnt[i] += cnt[i - 1];
		}
		for(int i = n - 1; i >= 0; --i) {
			cnt[rk[id[i]]]--;
			sa[cnt[rk[id[i]]]] = id[i];
		}
		p = 0;
		vector<int> oldrk = rk;
		rk[sa[0]] = 0;
		for(int i = 1; i < n; ++i) {
			if(oldrk[sa[i]] != oldrk[sa[i - 1]] ||
				oldrk[sa[i] + w] != oldrk[sa[i - 1] + w]) ++p;
			rk[sa[i]] = p;
		}
		if(p == n - 1) break;
	}
	return sa;
}

Height数组

vector<int> height(const string &str) {
	int n = str.size();
	vector<int> sa = suffix_array(str), rk(n), h(n);
	for(int i = 0; i < n; ++i) {
		rk[sa[i]] = i;
	}
	for(int i = 0, k = 0; i < n; ++i) {
		if(rk[i] == 0) continue;
		if(k > 0) --k;
		while(str[i + k] == str[sa[rk[i] - 1] + k]) ++k;
		h[rk[i]] = k;
	}
	return h;
}

后缀自动机

struct SAM {
	SAM() : nodes(1, Node(-1, 0)), last(0) {}
	void insert(char ch) {
		const int id = ch - 'a';
		const int cur = nodes.size();
		nodes.emplace_back(-1, nodes[last].len + 1);
		int p = last;
		while(p != -1 && nodes[p].nxt[id] == -1) {
			nodes[p].nxt[id] = cur;
			p = nodes[p].link;
		}
		if(p == -1) {
			nodes[cur].link = 0;
		} else {
			int q = nodes[p].nxt[id];
			if(nodes[p].len + 1 == nodes[q].len) {
				nodes[cur].link = q;
			} else {
				int clone = nodes.size();
				nodes.emplace_back(nodes[q].link, nodes[p].len + 1);
				nodes[clone].nxt = nodes[q].nxt;
				while(p != -1 && nodes[p].nxt[id] == q) {
					nodes[p].nxt[id] = clone;
					p = nodes[p].link;
				}
				nodes[q].link = nodes[cur].link = clone;
			}
		}
		last = cur;
		ends.push_back(cur);
	}
	// 洛谷P3804 求出 S 的所有出现次数不为 1 的子串的出现次数乘上该子串长度的最大值。
	ll solve() const {
		int n = nodes.size();
		vector<int> indeg(n, 0), topoorder;
		topoorder.reserve(n);
		for(int i = 0; i < n; ++i) {
			if(nodes[i].link != -1) {
				indeg[nodes[i].link]++;
			}
		}
		queue<int> q;
		for(int i = 0; i < n; ++i) {
			if(indeg[i] == 0) {
				q.push(i);
			}
		}
		while(!q.empty()) {
			int u = q.front();
			q.pop();
			topoorder.push_back(u);
			if(nodes[u].link != -1) {
				indeg[nodes[u].link]--;
				if(indeg[nodes[u].link] == 0) {
					q.push(nodes[u].link);
				}
			}
		}
		ll ans = 0;
		vector<ll> occur(n, 0);
		for(int e : ends) {
			occur[e] = 1;
		}
		for(int i : topoorder) {
			if(nodes[i].link != -1) {
				occur[nodes[i].link] += occur[i];
			}
			if(occur[i] != 1) {
				ans = max(ans, occur[i] * nodes[i].len);
			}
		}
		return ans;
	}
private:
	struct Node {
		array<int, 26> nxt;
		int link;
		int len;
		Node(int lk, int ln) : link(lk), len(ln) {
			nxt.fill(-1);
		}
	};
	vector<Node> nodes;
	vector<int> ends;
	int last;
};

序列自动机

struct SeqAM {
	explicit SeqAM(const string &s) : n(s.size()), nxt(s.size() + 2, [&] {
		array<int, 26> ret;
		ret.fill(s.size() + 1);
		return ret;
	}()) {
		for(int i = n - 1; i >= 0; --i) {
			nxt[i] = nxt[i + 1];
			nxt[i][s[i] - 'a'] = i + 1;
		}
	}
	bool match(const string &t) const {
		int now = 0;
		for(char c : t) {
			now = nxt[now][c - 'a'];
		}
		return now != (n + 1);
	}
private:
	int n;
	vector<array<int, 26>> nxt;
};

回文自动机

struct PAM {
	PAM() : s("#"), last(1) {
		nodes.emplace_back(-1, 0);
		nodes.emplace_back(0, 0);
	}
	int insert(char ch) {
		s += ch;
		int pos = s.size() - 1;
		int p = last, id = ch - 'a';
		while(s[pos - nodes[p].len - 1] != ch) {
			p = nodes[p].fail;
		}
		if(nodes[p].nxt[id] == -1) {
			int cur = nodes.size();
			nodes[p].nxt[id] = cur;
			nodes.emplace_back(nodes[p].len + 2, 0);
			if(nodes[cur].len == 1) {
				nodes[cur].fail = 1;
			} else {
				int f = nodes[p].fail;
				while(s[pos - nodes[f].len - 1] != ch) {
					f = nodes[f].fail;
				}
				nodes[cur].fail = nodes[f].nxt[id];
			}
			if(nodes[cur].len == 1) {
				nodes[cur].cnt = 1;
			} else {
				nodes[cur].cnt = nodes[nodes[cur].fail].cnt + 1;
			}
		}
		last = nodes[p].nxt[id];
		return nodes[last].cnt;
	}
private:
	struct Node {
		int len, fail, cnt;
		array<int, 26> nxt;
		Node(int l, int f) : len(l), fail(f), cnt(0) {
			nxt.fill(-1);
		}
	};
	vector<Node> nodes;
	string s;
	int last;
};

字符串哈希

struct StringHash {
	explicit StringHash(const string &s) : p1(s.size() + 1, 0), p2(s.size() + 1, 0) {
		for(int i = 0; i < s.size(); ++i) {
			p1[i + 1] = (p1[i] * mul1 + s[i]) % modulo;
			p2[i + 1] = p2[i] * mul2 + s[i];
		}
	}
	ull substr(int l, int r) const {
		ull ret1 = (p1[r] - p1[l] * pmul1[r - l] % modulo + modulo) % modulo;
		ull ret2 = p2[r] - p2[l] * pmul2[r - l];
		return (ret1 << 3) ^ (ret1 >> 5) ^ ret2;
	}
private:
	vector<ull> p1, p2;
	static inline const ull c = (ull)chrono::steady_clock::
		now().time_since_epoch().count();
	static inline const ull mul1 = c % 131 + 131, mul2 = c % 13331 + 13331;
	static inline ull pmul1[maxn], pmul2[maxn];
	static inline int init = [&] {
		pmul1[0] = pmul2[0] = 1;
		for(int i = 1; i < maxn; ++i) {
			pmul1[i] = pmul1[i - 1] * mul1 % modulo;
			pmul2[i] = pmul2[i - 1] * mul2;
		}
		return 0;
	}();
};

Lyndon分解

vector<string> duval(const string &s) {
	int n = s.size();
	vector<string> ret;
	for(int i = 0; i < n;) {
		int j = i + 1, k = i;
		while(j < n && s[k] <= s[j]) {
			(s[k] < s[j]) ? (k = i) : (k++);
			++j;
		}
		while(i <= k) {
			ret.push_back(s.substr(i, j - k));
			i += (j - k);
		}
	}
	return ret;
}

最小表示法

string minrep(const string &str) {
	int n = str.size(), i = 0, j = 1, k = 0;
	string s = str + str;
	while(i < n && j < n) {
		while(k < n && s[i + k] == s[j + k]) ++k;
		if(k == n) break;
		if(s[i + k] > s[j + k]) i += (k + 1);
		else j += (k + 1);
		if(i == j) ++j;
		k = 0;
	}
	return s.substr(min(i, j), n);
}

贪心

一组交叉直线经过所有点

struct Point {
	int x, y;
};
bool cross_all(const vector<Point> &points) {
	bool okx = true, oky = true;
	int xb = -1, yb = -1;
	for(auto [x, y] : points) { 
		if(x != points[0].x) {
			if(yb == -1) {
				yb = y;
			} else if(yb != y) {
				okx = false;
			}
		}
		if(y != points[0].y) {
			if(xb == -1) {
				xb = x;
			} else if(xb != x) {
				oky = false;
			}
		}
	}
	return okx || oky;
}

二分

最长上升子序列

vector<int> lis(const vector<int> &nums) {
	int n = nums.size();
	vector<int> dp, pos(n), pre(n, -1);
	for(int i = 0; i < n; ++i) {
		auto it = lower_bound(dp.begin(), dp.end(), nums[i]);
		if(it == dp.end()) dp.push_back(nums[i]);
		else *it = nums[i];
		int j = it - dp.begin();
		pos[j] = i;
		if(j != 0) pre[i] = pos[j - 1];
	}
	vector<int> ret;
	for(int i = pos[dp.size() - 1]; i != -1; i = pre[i]) {
		ret.push_back(nums[i]);
	}
	reverse(ret.begin(), ret.end());
	return ret;
}

二分答案

template<class T> T binary(auto &&check, bool rev, T minv, T maxv, T eps, T delta) {
	T ans = (rev ? minv : maxv), hi = maxv, lo = minv;
	while((hi - lo) >= eps) {
		T mid = (lo + hi) / 2;
		if(check(mid)) {
			ans = mid;
			rev ? (lo = mid + delta) : (hi = mid - delta);
		} else {
			rev ? (hi = mid - delta) : (lo = mid + delta);
		}
	}
	return ans;
}

三分

int peak(const vector<int> &nums) {
	int n = nums.size();
	int l = 0, r = n - 1;
	int ll = (r - l) / 3, rr = (r - l) * 2 / 3;
	while(l < r) {
		if(nums[ll] < nums[rr]) {
			l = ll + 1;
		} else {
			r = rr;
		}
		ll = l + (r - l) / 3;
		rr = l + (r - l) * 2 / 3;
	}
	return l;
}

杂项

高精

struct HPINT {
	HPINT() : nums(1, 0), neg(false) {}
	// intentionally implicit, allowing HPINT a = 5
	HPINT(ll val) : neg(false) {
		if(val < 0) {
			neg = true;
			val = -val;
		}
		while(val) {
			int t = val % 10;
			nums.push_back(t);
			val /= 10;
		}
		if(nums.empty()) {
			nums = vector<int>(1, 0);
		}
	}
	// and HPINT b = "11451419198103141592653589793238462621718281828"
	HPINT(const string &s) {
		int off = 0, n = s.size();
		if(s[0] == '-') {
			off = 1;
			neg = true;
		} else {
			neg = false;
		}
		for(; off < n; ++off) {
			nums.push_back(s[off] - '0');
		}
		reverse(nums.begin(), nums.end());
	}
	explicit operator string() const {
		string ret;
		if(neg) {
			ret += '-';
		}
		for(int i = nums.size() - 1; i >= 0; --i) {
			ret += (nums[i] + '0');
		}
		return ret;
	}
	HPINT &operator+=(const HPINT &r) {
		if(neg) {
			if(r.neg) {
				_add(nums, r.nums);
			} else {
				if(_cmp(nums, r.nums) == 1) {
					_sub(nums, r.nums);
				} else {
					vector<int> rnums = r.nums;
					_sub(rnums, nums);
					nums.swap(rnums);
					neg = false;
				}
			}
		} else {
			if(r.neg) {
				if(_cmp(nums, r.nums) != -1) {
					_sub(nums, r.nums);
				} else {
					vector<int> rnums = r.nums;
					_sub(rnums, nums);
					nums.swap(rnums);
					neg = true;
				}
			} else {
				_add(nums, r.nums);
			}
		}
		return *this;
	}
	HPINT &operator-=(const HPINT &r) {
		if(neg) {
			if(r.neg) {
				int c = _cmp(nums, r.nums);
				if(c == 1) {
					_sub(nums, r.nums);
				} else {
					vector<int> rnums = r.nums;
					_sub(rnums, nums);
					nums.swap(rnums);
					neg = false;
				}
			} else {
				_add(nums, r.nums);
			}
		} else {
			if(r.neg) {
				_add(nums, r.nums);
			} else {
				int c = _cmp(nums, r.nums);
				if(c == -1) {
					vector<int> rnums = r.nums;
					_sub(rnums, nums);
					nums.swap(rnums);
					neg = true;
				} else {
					_sub(nums, r.nums);
				}
			}
		}
		return *this;
	}
	HPINT &operator*=(const HPINT &r) {
		neg ^= r.neg;
		_mul(nums, r.nums);
		return *this;
	}
	friend ostream &operator<<(ostream &os, const HPINT &hp) {
		if(hp.neg) {
			os << '-';
		}
		for(int i = hp.nums.size() - 1; i >= 0; --i) {
			os << hp.nums[i];
		}
		return os;
	}
	friend istream &operator>>(istream &is, HPINT &hp) {
		char ch = is.rdbuf()->sbumpc();
		while(ch < '0' || ch > '9') {
			if(ch == '-') {
				hp.neg = true;
			}
			ch = is.rdbuf()->sbumpc();
		}
		string buffer;
		while(ch >= '0' && ch <= '9') {
			buffer += ch;
			ch = is.rdbuf()->sbumpc();
		}
		hp.nums.resize(buffer.size());
		int n = buffer.size();
		for(int i = 0; i < n; ++i) {
			hp.nums[i] = buffer[n - i - 1] - '0';
		}
		return is;
	}
	bool operator==(const HPINT &r) const {
		return neg == r.neg && _cmp(nums, r.nums) == 0;
	}
	strong_ordering operator<=>(const HPINT &r) const {
		if(neg != r.neg) {
			if(neg) return strong_ordering(-1);
			return strong_ordering(1);
		}
		int c = _cmp(nums, r.nums);
		if(neg) c *= -1;
		return strong_ordering(c);
	}
	HPINT operator+(const HPINT &r) const {
		HPINT ret = *this;
		ret += r;
		return ret;
	}
	HPINT operator-(const HPINT &r) const {
		HPINT ret = *this;
		ret -= r;
		return ret;
	}
	HPINT operator*(const HPINT &r) const {
		HPINT ret = *this;
		ret *= r;
		return ret;
	}
private:
	vector<int> nums;
	bool neg;
	static void _add(vector<int> &a, const vector<int> &b) {
		int carry = 0;
		for(int i = 0; i < b.size(); ++i) {
			if(i >= a.size()) {
				a.push_back(0);
			}
			a[i] += (b[i] + carry);
			carry = 0;
			if(a[i] >= 10) {
				carry = 1;
				a[i] -= 10;
			}
		}
		int i = b.size();
		while(carry != 0) {
			if(i >= a.size()) {
				a.push_back(0);
			}
			a[i] += carry;
			carry = 0;
			if(a[i] >= 10) {
				carry = 1;
				a[i] -= 10;
			}
			++i;
		}
	}
	static void _sub(vector<int> &a, const vector<int> &b) {
		int borrow = 0;
		for(int i = 0; i < b.size(); ++i) {
			a[i] -= (borrow + b[i]);
			borrow = 0;
			if(a[i] < 0) {
				a[i] += 10;
				borrow = 1;
			}
		}
		int i = b.size();
		while(borrow && i < a.size()) {
			a[i] -= borrow;
			borrow = 0;
			if(a[i] < 0) {
				a[i] += 10;
				borrow = 1;
			}
			i++;
		}
		while(a.size() > 1 && a.back() == 0) {
			a.pop_back();
		}
	}
	static void _mul(vector<int> &a, const vector<int> &b) {
		using cd = complex<double>;
		constexpr double pi = 3.14159265358979323846264338327950288;
		int n = 1;
		while(n < (int)(a.size() + b.size())) {
			n <<= 1;
		}
		vector<cd> fa(n), fb(n);
		for(int i = 0; i < (int)a.size(); ++i) {
			fa[i] = a[i];
		}
		for(int i = 0; i < (int)b.size(); ++i) {
			fb[i] = b[i];
		}
		auto fft = [](auto &&fft, vector<cd> &f, bool invert) -> void {
			int n = f.size();
			if(n == 1)
				return;
			vector<cd> f0(n / 2), f1(n / 2);
			for(int i = 0; i < n / 2; ++i) {
				f0[i] = f[2 * i];
				f1[i] = f[2 * i + 1];
			}
			fft(fft, f0, invert);
			fft(fft, f1, invert);
			double theta = 2 * pi / n * (invert ? -1 : 1);
			cd wt = 1, w(cos(theta), sin(theta));
			for(int t = 0; t < n / 2; ++t) {
				cd u = f0[t], v = wt * f1[t];
				f[t] = u + v;
				f[t + n / 2] = u - v;
				wt *= w;
			}
		};
		fft(fft, fa, false);
		fft(fft, fb, false);
		for(int i = 0; i < n; ++i) {
			fa[i] *= fb[i];
		}
		fft(fft, fa, true);
		a.resize(n);
		int carry = 0;
		for(int i = 0; i < n; ++i) {
			int num = int(fa[i].real() / n + 0.5) + carry;
			carry = num / 10;
			a[i] = num % 10;
		}
		while(carry) {
			a.push_back(carry % 10);
			carry /= 10;
		}
		while(a.size() > 1 && a.back() == 0) {
			a.pop_back();
		}
	}
	// 1 gt, -1 lt, 0 eq
	static int _cmp(const vector<int> &a, const vector<int> &b) {
		if(a.size() > b.size()) return 1;
		if(a.size() < b.size()) return -1;
		for(int n = a.size(), i = n - 1; i >= 0; --i) {
			if(a[i] > b[i]) return 1;
			if(a[i] < b[i]) return -1;
		}
		return 0;
	}
};

快读

struct Qread {
	Qread() : state(true) {
		cin.rdbuf()->pubsetbuf(buffer, maxn);
	}
	template<integral T> Qread &operator>>(T &val) {
		if(!state) {
			val = 0;
			return *this;
		}
		T x = 0, f = 1;
		char ch = cin.rdbuf()->sbumpc();
		while(ch < '0' || ch > '9') {
			if(ch == char_traits<char>::eof()) {
				state = false;
				cin.setstate(ios_base::eofbit);
				val = x * f;
				return *this;
			}
			if(ch == '-') {
				f = -1;
			}
			ch = cin.rdbuf()->sbumpc();
		}
		while(ch >= '0' && ch <= '9') {
			x = x * 10 + ch - '0';
			ch = cin.rdbuf()->sbumpc();
		}
		val = x * f;
		return *this;
	}
	explicit operator bool() const {
		return state;
	}
private:
	bool state;
	static constexpr int maxn = 1 << 21;
	char buffer[maxn];
}qread;

自定义哈希

struct MyHash {
	size_t operator()(ll x) const noexcept {
		x ^= (x >> 21);
		x ^= (x << 37);
		x ^= (x >> 4);
		x *= 0x27d4eb2f165667c5;
		x ^= (x >> 28);
		x *= 0x165667b19e3779f9;
		x ^= (x >> 31);
		return x ^ c;
	}
private:
	static inline size_t c = (size_t)chrono::steady_clock::now().time_since_epoch().count();
};
posted @ 2025-09-05 23:57  氧烷OH2  阅读(11)  评论(0)    收藏  举报