个人算法竞赛模板(2024.5.22)
精简版:
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#include <bitset>
#include <numeric>
#define fi first
#define se second
using namespace std;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ull P = 131;
const double eps = 1e-4;
const int N = 0;
void solve() {
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
// multiple case
// int t; scanf("%d", &t);
// while(t--) {
// solve();
// }
// single case
solve();
return 0;
}
对拍模板(python)
import random
import os
import filecmp
import string
# 造数据
def makedata():
# [a, b]区间内生成整数
n = random.randint(1, 1e6)
k = random.randint(1, 1e10-1)
# [a, b)区间生成随机偶数
print(random.range(0, 100001, 2))
# [0, 1)区间生成随机浮点数
print(random.random())
# [a, b]区间生成随机浮点数
print(random.uniform(1, 20))
# 指定字符集中随机生成一个字符
print(random.choice('abcdefghijklmnopqrst@#$%*()'))
# 指定字符集中随机生成指定数量的字符
print(random.sample('abcdefghijklmnopqrst@#$%*()', 5))
# 用a~z、A~Z、0~9生成指定数量的随机字符串
ran_s = ''.join(random.sample(string.ascii_letters + string.digits, 7))
# 从多个字符中选取指定数量的字符组成新字符串
print(''.join(random.sample(['m', 'l', 'i', 'h', 'g', 'k', 'j', 'd'])))
# 打算顺序
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
random.shuffle(items)
return n, k
# 使用set去重(小数去重为每个数除以100)
def NoRepeatList(data):
data = list(set(data))
random.shuffle(data)
return data
def check(n, k):
with open('./g.in', 'w') as file:
s = str(n) + ' ' + str(k)
file.write(s)
os.system("g.exe < g.in > g.out")
os.system("g_loliconsk.exe < g.in > g_loliconsk.out")
if not filecmp.cmp("g.out", "g_loliconsk.out"):
print(n, k, "不相同")
exit(0)
def duipai_1():
while True:
n, k = makedata()
check(n, k)
def duipai_2():
for n in range(1, int(1e10+1)):
for k in range(1, n+1):
check(n, k)
if __name__ == '__main__':
choice = int(input())
if choice == 1:
duipai_1()
else:
duipai_2()
完整版
#pragma GCC optimize(2)
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#include <bitset>
#include <numeric>
#define fi first
#define se second
#define endl '\n'
using namespace std;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
const ll ll_inf = 0x3f3f3f3f3f3f3f3f;
const int int_inf = 0x3f3f3f3f;
const ull P = 131;
const double eps = 1e-4;
const int N = 0, mod = 1;
/**
* 质数 筛法
* 约数 个数
* 欧拉函数
* 求组合数
* 扩展欧几里得
* 线性同余方程
* 高斯消元解线性方程组
* 数学期望
* 并查集
* 字符串哈希
* Z函数(扩展KMP)
* 树状数组
* 线段树
* 强联通分量Tarjan
* 2-sat问题
* lca最近公共祖先
* 区间合并
* 高精度算法
* 归并排序
* 快速排序
* 堆
* 字典树Trie
* 双链表
* 匈牙利算法
* 染色法判定二分图
* 最小生成树 kruskal prim
* 最短路算法 dijkstra bellman-ford spfa prim
* 区间选点 最大不相交区间数量 区间分组 区间覆盖线段
* RMQ
* DP LIS(最长递增子序列)
* 计算几何部分
*/
// 质数 线性筛
vector<int> get_prime() {
const int N = 1e5 + 10;
vector<int> primes;
vector<bool> st(N, false);
for(int i=2; i < N; i++){
if(!st[i]) primes.push_back(i);
for(int j=0; primes[j] < N/i; j++){
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
return primes;
}
// 约数个数 ai乘积的约数个数
ll yueshu_cnt(vector<ll> &a) {
map<ll, ll> primes;
for (auto x : a) {
for (ll i=2; i<=x/i; i++) {
while(x%i == 0) {
x /= i;
primes[i]++;
}
}
if (x>1) primes[x]++;
}
ll res=1;
for (auto t:primes) res = res*(t.se + 1) % mod;
return res;
}
// 约数之和 ai乘积的约数之和
ll yueshu_sum(vector<ll> &a) {
map<ll, ll> primes;
for (auto x : a) {
for (ll i=2; i <= x / i; i++){
while (x % i == 0) {
x /= i;
primes[i]++;
}
}
if(x > 1) primes[x]++;
}
ll res = 1;
for (auto [q, c] : primes) {
ll tmp = 1;
while (c--) tmp = (tmp * q + 1) % mod;
res = res * tmp % mod;
}
return res;
}
// 欧拉函数
int euler(int x) {
int res = x;
for(int i=2; i<=x/i; i++){
if(x % i == 0){
while(x % i==0) x /= i;
//必须先/i再乘 否则可能溢出
res = res / i * (i - 1);
}
}
if(x > 1) res = res / x * (x - 1);
return res;
}
// 筛法求1~n的欧拉函数
vector<ll> get_eulers(int n) {
vector<ll> phi(n + 1, 0);
vector<bool> st(n + 1, false);
vector<int> primers;
phi[1] = 1;
for(int i=2; i<=n; i++) {
if(!st[i]) {
primers.push_back(i);
phi[i] = i-1;
}
for(int j = 0; primers[j] <= n / i; j++) {
int t = primers[j] * i;
st[t] = true;
if(i % primers[j] == 0) {
phi[t] = phi[i] * primers[j];
break;
}
phi[t] = phi[i] * (primers[j] - 1);
}
}
return phi;
}
// 快速幂
ll qmi(ll a, ll b) {
a %= mod;
ll res = 1;
while (b) {
if (b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
// 求组合数(a, b <= 1e18) 按照定义直接求
ll C(ll a, ll b) {
ll res = 1;
for (ll i=1, j=a; i<=b; i++, j--) {
res = res * j % mod;
res = res * qmi(i, mod - 2) % mod;
}
return res;
}
// 求组合数(n <= 1000) 按照推到关系
ll c[N][N];
void init() {
for(int i=0; i<N; i++)
for(int j=0; j<=i; j++)
if(!j) c[i][j] = 1;
else c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
}
// 求组合数(a,b <= 10000) 按照定义直接求
ll C_2(ll a, ll b) {
// 初始化部分
const int N = 1e5+10;
vector<ll> fact(N, 0), infact(N, 0);
fact[0] = infact[0] = 1;
for(int i=1; i<N; i++){
fact[i] = fact[i-1] * i % mod;
infact[i] = infact[i-1] * qmi(i, mod-2) % mod;
}
// 真正求的部分
return fact[a] * infact[b] % mod * infact[a-b] % mod;
}
// 扩展欧几里得算法
ll exgcd(ll a, ll b, ll &x, ll &y) {
if(!b) {
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a%b, y, x);
y -= a/b * x;
return d;
}
// 线性同余方程 a * x == b (mod m)
ll xianxingtongyu(ll a, ll b, ll m) {
ll x, y;
int t = exgcd(a, m, x, y);
if(b % t) return -1;
// x*(b/t)是因为 求出来的x对应的d是最大公约数 而b是最大公约数的b/t倍
return x * (b/t) % m;
}
// 高斯消元解线性方程组
// 返回0表示只有1个解,1表示无数组解,其余表示无解
int guess(int n, vector<vector<double>> &a) {
int r, c;
for(r=c=1; c<=n; c++) {
//1. 找出绝对值最大
int u = r;
for(int i=r; i<=n; i++)
if(fabs(a[i][c]) > fabs(a[u][c])) u = i;
if(fabs(a[u][c]) < eps) continue;
//2.将该行换到最上面
for(int i=c; i<=n+1; i++) swap(a[u][i], a[r][i]);
//3.将该行当前列变为1
for(int i=n+1; i>=c; i--) a[r][i] /= a[r][c];
//4.将剩余行当前列变为0
for(int i=r+1; i<=n; i++)
if(fabs(a[i][c]) > eps)
for(int j=n+1; j>=c; j--)
a[i][j] -= a[i][c] * a[r][j];
r++;
}
if(r<=n) {
for(int i=r; i<=n; i++) if(fabs(a[i][n+1]) > eps) return -1;
return 1;
}
//5.迭代求出x_i
for(int i=n; i>=1; i--)
for(int j=i+1; j<=n; j++)
a[i][n+1] -= a[i][j] * a[j][n+1];
return 0;
}
// 数学期望例题 绿豆蛙从起点走到终点的数学期望
// 每条路选择概率为1/k
void shuxueqiwang_demo() {
vector<pii> e[N]; // {下一个点,路径长度}
double f[N];
int chudu[N]; // 出度
auto dfs = [&](auto self, int u) ->double {
if (f[u] >= 0) return f[u];
f[u] = 0;
for (auto [v, w] : e[u]) {
f[u] += (self(self, v) + w) / chudu[u];
}
return f[u];
};
}
// 并查集
struct DSU {
vector<int> p;
int n;
DSU(int _n) : n(_n) {
p.resize(n + 1);
iota(p.begin(), p.end(), 0);
}
inline int find(int x) {
return (x == p[x] ? x : (p[x] = find(p[x])));
}
inline bool unite(int x, int y) {
x = find(x);
y = find(y);
if (x != y) {
p[x] = y;
return true;
}
return false;
}
};
// 字符串哈希
struct StringHash {
static ull P1, P2;
vector<ull> h1, p1, h2, p2;
// s从1开始为真正的字符串
StringHash(int n, string &s) {
h1.resize(n + 1);
p1.resize(n + 1);
h2.resize(n + 1);
p2.resize(n + 1);
h1[0] = h2[0] = 0;
p1[0] = p2[0] = 1;
for (int i=1; i<=n; i++) {
h1[i] = h1[i-1] * P1 + s[i] - 'a' + 1;
p1[i] = p1[i-1] * P1;
h2[i] = h2[i-1] * P2 + s[i] - 'a' + 1;
p2[i] = p2[i-1] * P2;
}
}
ull gethash1(int l, int r) {
return h1[r] - h1[l - 1] * p1[r - l + 1];
}
ull gethash2(int l, int r) {
return h2[r] - h2[l - 1] * p2[r - l + 1];
}
bool strHashEql(int l1, int r1, int l2, int r2) {
return gethash1(l1, r1) == gethash1(l2, r2) && gethash2(l1, r1) == gethash2(l2, r2);
}
};
ull StringHash::P1 = 1313141;
ull StringHash::P2 = 233341;
// Z函数(扩展KMP) 字符下标从0开始
vector<int> z_function(string &s) {
int n = (int)s.length();
vector<int> z(n, 0);
z[0] = 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;
}
inline int lowbit(int x){
return x & -x;
}
// 树状数组
struct TreeArray {
vector<int> tr;
int n;
TreeArray(int n) {
tr.resize(n+1, 0);
this->n = n;
}
void add(int x, int c) {
for(int i=x; i<=n; i+=lowbit(i)) tr[i] += c;
}
int sum(int x) {
int res = 0;
for(int i=x; i; i-=lowbit(i)) res += tr[i];
return res;
}
};
// 线段树
struct SegmentTree {
struct Node{
int l, r;
ll sum, add;
};
vector<Node> tr;
void init(int n, vector<int> &a) {
tr.resize(n * 4 + 1);
build(1, 1, n, a);
}
inline int lchild(int u) {
return u << 1;
}
inline int rchild(int u) {
return u << 1 | 1;
}
void pushup(int u) {
tr[u].sum = tr[lchild(u)].sum + tr[rchild(u)].sum;
}
void pushdown(int u) {
auto &root = tr[u], &left = tr[lchild(u)], &right = tr[rchild(u)];
if(!root.add) return;
left.add += root.add;
left.sum += (ll)(left.r - left.l + 1) * root.add;
right.add += root.add;
right.sum += (ll)(right.r - right.l + 1) * root.add;
root.add = 0;
}
void build(int u, int l, int r, vector<int> &a) {
if(l == r) tr[u] = {l, r, a[l], 0};
else {
tr[u] = {l, r};
int mid = (l + r) >> 1;
build(lchild(u), l, mid, a), build(rchild(u), mid+1, r, a);
pushup(u);
}
}
// 区间修改
void modify(int u, int l, int r, int v) {
if(tr[u].l >= l && tr[u].r <= r){
tr[u].sum += (ll)(tr[u].r - tr[u].l + 1) * v;
tr[u].add += v;
}
else {
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid) modify(lchild(u), l, r, v);
if(r > mid) modify(rchild(u), l, r, v);
pushup(u);
}
}
// 单点修改
void modify(int u, int x, ll v) {
if(tr[u].l == tr[u].r) {
tr[u].sum += v;
}
else {
int mid = (tr[u].l + tr[u].r) >> 1;
if(x <= mid) modify(lchild(u), x, v);
else modify(rchild(u), x, v);
pushup(u);
}
}
ll query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = (tr[u].l + tr[u].r) >>1;
ll res = 0;
if(l <= mid) res = query(lchild(u), l, r);
if(r > mid) res += query(rchild(u), l, r);
return res;
}
};
// 有向图强联通分量 Tarjan
struct SCC {
int n;
int timestap, scc_cnt, tt;
vector<int> dfn, low, cnt, id, stk, in_stk;
vector<vector<int>>& e;
SCC(int n, vector<vector<int>> &e) : n(n), e(e) {
dfn = low = cnt = id = stk = in_stk = vector<int>(n + 1, 0);
timestap = scc_cnt = tt = 0;
}
void tarjan(int u) {
dfn[u] = low[u] = ++timestap;
stk[++tt] = u;
in_stk[u] = true;
for (auto v : e[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (in_stk[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]) {
int y;
++ scc_cnt;
do {
y = stk[tt--];
in_stk[y] = false;
cnt[scc_cnt] ++;
id[y] = scc_cnt;
} while (y != u);
}
}
};
// 2-sat问题 节点下标从0开始
bool TwoSAT(int n, vector<vector<int>>& e) {
vector<int> x;
// (x[i]=a || x[j]=b) == true
// while (m --) {
// int i, a, j, b;
// scanf("%d%d%d%d", &i, &a, &j, &b);
// i --; j --;
// e[2 * i + !a].push_back(2 * j + b);
// e[2 * j + !b].push_back(2 * i + a);
// }
SCC scc(2 * n, e);
for (int i=0; i<2*n; i++)
if (!scc.dfn[i])
scc.tarjan(i);
for (int i=0; i<n; i++) {
if (scc.id[2 * i] == scc.id[2 * i + 1]) return false;
}
for (int i=0; i<n; i++) {
if (scc.id[2 * i] < scc.id[2 * i + 1]) x.push_back(0);
else x.push_back(1);
}
return true;
}
// st表跳表 LCA 最近公共祖先
struct LCA {
const int LCA_M = 20; // 2^M个节点(1e6)
int n;
vector<int> dep;
vector<vector<int>> &e;
vector<vector<int>> fa;
LCA(int _n, vector<vector<int>> &_e) : n(_n), e(_e) {
dep = vector<int>(_n + 1, 0);
fa = vector<vector<int>>(_n + 1, vector<int>(LCA_M, 0));
}
void dfs(int u, int pa) {
dep[u] = dep[pa] + 1;
fa[u][0] = pa;
for (int i=1; i<LCA_M; i++) {
fa[u][i] = fa[fa[u][i-1]][i-1];
}
for (auto v : e[u]) {
if (v == pa) continue;
dfs(v, u);
}
}
int lca(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
for (int i=LCA_M-1; i>=0; i--) {
if (dep[fa[x][i]] >= dep[y]) {
x = fa[x][i];
}
}
if (x == y) return x;
for (int i=LCA_M-1; i>=0; i--) {
if (fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}
int dis(int x, int y) {
int ac = lca(x, y);
return dep[x] + dep[y] - 2 * dep[ac];
}
};
// 区间合并
vector<pii> merge(vector<pii> &segs) {
vector<pii> res;
sort(segs.begin(), segs.end());
int st = -2e9, ed = -2e9;
for(auto seg:segs) {
if(ed < seg.fi) {
if(st != -2e9) res.push_back({st, ed});
st = seg.fi; ed = seg.se;
}
else ed = max(ed, seg.se);
}
if(st != -2e9) res.push_back({st, ed});
return res;
}
// 高精度
struct BigIntTiny {
int sign;
std::vector<int> v;
BigIntTiny() : sign(1) {}
BigIntTiny(const std::string &s) { *this = s; }
BigIntTiny(int v) {
char buf[21];
sprintf(buf, "%d", v);
*this = buf;
}
void zip(int unzip) {
if (unzip == 0) {
for (int i = 0; i < (int)v.size(); i++)
v[i] = get_pos(i * 4) + get_pos(i * 4 + 1) * 10 + get_pos(i * 4 + 2) * 100 + get_pos(i * 4 + 3) * 1000;
} else
for (int i = (v.resize(v.size() * 4), (int)v.size() - 1), a; i >= 0; i--)
a = (i % 4 >= 2) ? v[i / 4] / 100 : v[i / 4] % 100, v[i] = (i & 1) ? a / 10 : a % 10;
setsign(1, 1);
}
int get_pos(unsigned pos) const { return pos >= v.size() ? 0 : v[pos]; }
BigIntTiny &setsign(int newsign, int rev) {
for (int i = (int)v.size() - 1; i > 0 && v[i] == 0; i--)
v.erase(v.begin() + i);
sign = (v.size() == 0 || (v.size() == 1 && v[0] == 0)) ? 1 : (rev ? newsign * sign : newsign);
return *this;
}
std::string to_str() const {
BigIntTiny b = *this;
std::string s;
for (int i = (b.zip(1), 0); i < (int)b.v.size(); ++i)
s += char(*(b.v.rbegin() + i) + '0');
return (sign < 0 ? "-" : "") + (s.empty() ? std::string("0") : s);
}
bool absless(const BigIntTiny &b) const {
if (v.size() != b.v.size()) return v.size() < b.v.size();
for (int i = (int)v.size() - 1; i >= 0; i--)
if (v[i] != b.v[i]) return v[i] < b.v[i];
return false;
}
BigIntTiny operator-() const {
BigIntTiny c = *this;
c.sign = (v.size() > 1 || v[0]) ? -c.sign : 1;
return c;
}
BigIntTiny &operator=(const std::string &s) {
if (s[0] == '-')
*this = s.substr(1);
else {
for (int i = (v.clear(), 0); i < (int)s.size(); ++i)
v.push_back(*(s.rbegin() + i) - '0');
zip(0);
}
return setsign(s[0] == '-' ? -1 : 1, sign = 1);
}
bool operator<(const BigIntTiny &b) const {
return sign != b.sign ? sign < b.sign : (sign == 1 ? absless(b) : b.absless(*this));
}
bool operator==(const BigIntTiny &b) const { return v == b.v && sign == b.sign; }
BigIntTiny &operator+=(const BigIntTiny &b) {
if (sign != b.sign) return *this = (*this) - -b;
v.resize(std::max(v.size(), b.v.size()) + 1);
for (int i = 0, carry = 0; i < (int)b.v.size() || carry; i++) {
carry += v[i] + b.get_pos(i);
v[i] = carry % 10000, carry /= 10000;
}
return setsign(sign, 0);
}
BigIntTiny operator+(const BigIntTiny &b) const {
BigIntTiny c = *this;
return c += b;
}
void add_mul(const BigIntTiny &b, int mul) {
v.resize(std::max(v.size(), b.v.size()) + 2);
for (int i = 0, carry = 0; i < (int)b.v.size() || carry; i++) {
carry += v[i] + b.get_pos(i) * mul;
v[i] = carry % 10000, carry /= 10000;
}
}
BigIntTiny operator-(const BigIntTiny &b) const {
if (b.v.empty() || (b.v.size() == 1 && b.v[0] == 0)) return *this;
if (sign != b.sign) return (*this) + -b;
if (absless(b)) return -(b - *this);
BigIntTiny c;
for (int i = 0, borrow = 0; i < (int)v.size(); i++) {
borrow += v[i] - b.get_pos(i);
c.v.push_back(borrow);
c.v.back() -= 10000 * (borrow >>= 31);
}
return c.setsign(sign, 0);
}
BigIntTiny operator*(const BigIntTiny &b) const {
if (b < *this) return b * *this;
BigIntTiny c, d = b;
for (int i = 0; i < (int)v.size(); i++, d.v.insert(d.v.begin(), 0))
c.add_mul(d, v[i]);
return c.setsign(sign * b.sign, 0);
}
BigIntTiny operator/(const BigIntTiny &b) const {
BigIntTiny c, d;
BigIntTiny e=b;
e.sign=1;
d.v.resize(v.size());
double db = 1.0 / (b.v.back() + (b.get_pos((unsigned)b.v.size() - 2) / 1e4) +
(b.get_pos((unsigned)b.v.size() - 3) + 1) / 1e8);
for (int i = (int)v.size() - 1; i >= 0; i--) {
c.v.insert(c.v.begin(), v[i]);
int m = (int)((c.get_pos((int)e.v.size()) * 10000 + c.get_pos((int)e.v.size() - 1)) * db);
c = c - e * m, c.setsign(c.sign, 0), d.v[i] += m;
while (!(c < e))
c = c - e, d.v[i] += 1;
}
return d.setsign(sign * b.sign, 0);
}
BigIntTiny operator%(const BigIntTiny &b) const { return *this - *this / b * b; }
bool operator>(const BigIntTiny &b) const { return b < *this; }
bool operator<=(const BigIntTiny &b) const { return !(b < *this); }
bool operator>=(const BigIntTiny &b) const { return !(*this < b); }
bool operator!=(const BigIntTiny &b) const { return !(*this == b); }
};
// 归并排序
void merge_sort(int l, int r, int a[], int q[]) {
if(l >= r) return;
int mid = (l + r) >> 1;
merge_sort(l, mid, a, q);
merge_sort(mid + 1, r, a, q);
int i = l, j = mid + 1, k = l;
while(i <= mid && j <= r) {
if(q[i] <= q[j]) a[k++] = q[i++];
else a[k++] = q[j++];
}
while(i <= mid) a[k++] = q[i++];
while(j <= r) a[k++] = q[j++];
for(i = l; i <= r; i++) q[i] = a[i];
}
// 快速排序
void quick_sort(int l, int r, int q[]) {
if(l >= r) return;
int i = l - 1, j = r + 1, x = q[(l + r) >> 1];
while(i < j) {
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(l, j, q);
quick_sort(j+1, r, q);
}
// 堆
struct Heap {
int n, hsize;
vector<int> h;
Heap(int _n, vector<int> &_h) : n(_n), h(_h) {
hsize = n;
for(int i = n / 2; i; i--) down(i);
}
void up(int u) {
while(u/2 && h[u/2] > h[u]){
swap(h[u/2], h[u]);
u /= 2;
}
}
void down(int u) {
int t = u;
if(u*2 <= hsize && h[u*2] < h[t]) t = u*2;
if(u*2+1 <= hsize && h[u*2+1] < h[t]) t = u*2+1;
if(u!=t){
swap(h[u], h[t]);
down(t);
}
}
void push(int x) {
hsize++;
h.push_back(x);
up(hsize);
}
void pop() {
h[1] = h[hsize];
h.pop_back();
hsize--;
down(1);
}
int top() {
return h[1];
}
};
// 字典树
struct Trie {
const int Node_M = 26;
struct Node {
char c;
int cnt;
vector<int> ne;
};
vector<Node> pa;
int n, idx;
Trie(int _maxn) : n(_maxn) {
pa = vector<Node>(n + 1, {0, 0, vector<int>(Node_M, 0)});
}
void insert(string &s) {
int u = 0;
for (int i=0; i<(int)s.size(); i++) {
int c = s[i] - 'a';
if (!pa[u].ne[c]) {
pa[u].ne[c] = ++idx;
}
u = pa[u].ne[c];
}
pa[u].cnt ++; // 一般只加在最后节点
}
int query(string &s) {
int u = 0;
for (int i=0; i<(int)s.size(); i++) {
int c = s[i] - 'a';
if (!pa[u].ne[c]) return 0;
u = pa[u].ne[c];
}
return pa[u].cnt;
}
};
// 双链表 双向链表
struct DulList {
vector<int> l, r, e;
int maxn, idx;
DulList(int _maxn) :maxn(_maxn) {
l = r = e = vector<int>(maxn + 10, 0);
r[0] = 1; l[1] = 0;
idx = 2;
}
void add(int k, int x){
e[idx] = x;
r[idx] = r[k];
l[idx] = k;
l[r[k]] = idx;
r[k] = idx++;
}
void remove(int k){
l[r[k]] = l[k];
r[l[k]] = r[k];
}
};
// 匈牙利算法 二分图最大匹配 边:只有左边的点->右边的点
int HungarianAlgorithm(int n1, int n2, vector<vector<int>> &e) {
vector<bool> st;
vector<int> match = vector<int>(n2 + 1, 0);
auto find = [&](auto self, int u) -> bool {
for(auto v : e[u]) {
if(st[v]) continue;
st[v] = true;
if(!match[v] || self(self, match[v])) {
match[v] = u;
return true;
}
}
return false;
};
int res = 0;
for(int i=1; i<=n1; i++) {
st = vector<bool>(n2 + 1, false);
if(find(find, i)) res++;
}
return res;
}
// 染色法判定二分图
bool checkBipartiteGraph(int n, vector<vector<int>> &e) {
vector<int> color(n + 1, 0);
auto dfs = [&](auto self, int u, int c) -> bool {
color[u] = c;
for(auto v : e[u]) {
if(!color[v])
if(!self(self, v, 3-c)) return false;
if(color[v] != 3-c) return false;
}
return true;
};
bool flag = true;
for(int i=1; i<=n; i++) {
if(!color[i]){
flag = dfs(dfs, i, 1);
if(!flag) break;
}
}
return flag;
}
// 最小生成树 kruskal算法
struct Edge {
int a, b, w;
const bool operator< (const Edge& A) const {
return w < A.w;
}
};
int kruskal(int n, vector<Edge> edges) {
DSU dsu(n);
int cnt = 0, res = 0;
sort(edges.begin(), edges.end());
for(int i=0; i<(int)edges.size(); i++){
auto [a, b, w] = edges[i];
if (dsu.unite(a, b)) {
res += w;
cnt++;
}
}
if(cnt < n-1) return 0x3f3f3f3f; // 不连通
else return res;
}
// 最小生成树 prim算法 粘稠图
int prim(int n, vector<vector<int>> &g) {
const int inf = 0x3f3f3f3f;
vector<int> dist(n + 1, inf);
vector<bool> st(n + 1, false);
int res = 0;
for(int i=0; i<n; i++){
int t=-1;
for(int j=1; j<=n; j++)
if(!st[j] && (t==-1 || dist[t]>dist[j]))
t = j;
st[t] = true;
if(i && dist[t] == inf) return inf;
if(i) res += dist[t];
for(int j=1; j<=n; j++) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
// 单源最短路算法 dijkstra
int dijkstra(int n, vector<vector<pii>> &e) {
const int inf = 0x3f3f3f3f;
vector<int> dist(n + 1, inf);
vector<bool> st(n + 1, false);
priority_queue<pii, vector<pii>, greater<pii>> hp;
dist[1] = 0; hp.push({0, 1});
while(hp.size()) {
auto [dis, u] = hp.top(); hp.pop();
if (st[u]) continue;
st[u] = true;
for(auto [v, w] : e[u]) {
if(dist[v] > dis + w){
dist[v] = dis + w;
hp.push({dist[v], v});
}
}
}
return dist[n];
}
// 单源最短路算法 bellman-ford 最多走k条边
int bellman_ford(int n, vector<Edge> &edges, int k) {
const int inf = 0x3f3f3f3f;
vector<int> dist(n + 1, inf), backup;
vector<bool> st(n + 1, false);
dist[1] = 0;
for(int i=0; i<k; i++) {
backup = dist;
for(int j=0; j<(int)edges.size(); j++){
auto [a, b, w] = edges[j];
dist[b] = min(dist[b], backup[a] + w);
}
}
return dist[n];
}
// 单源最短路算法 spfa
// 判断负环就看到点v是否经过了n个点
int spfa(int n, vector<vector<pii>> &e) {
const int inf = 0x3f3f3f3f;
vector<int> dist(n + 1, inf);
vector<bool> st(n + 1, false);
dist[1] = 0;
queue<int> q;
q.push(1); st[1] = true;
while(q.size()) {
int u = q.front(); q.pop(); st[u] = false;
for(auto [v, w] : e[u]) {
if(dist[v] > dist[u] + w){
dist[v] = dist[u] + w;
if(!st[v]){
q.push(v); st[v] = true;
}
}
}
}
return dist[n];
}
// 多远最短路算法 floyd
void floyd(int n, vector<vector<int>> &d){
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
// 区间选点 给多个闭区间 [l,r] 在数轴上选最少的点
// 使得每个区间内至少包含一个选出的点
// 最大不相交区间数量 同下 按照右端点升序排序
int range_point(vector<pii> &range) {
int n = range.size();
sort(range.begin(), range.end(), [&](const pii &a, const pii &b) {
return a.se < b.se;
});
int res = 0, ed=-2e9;
for (int i=0; i<n; i++) {
if (range[i].fi > ed) {
res++;
ed = range[i].se;
}
}
return res;
}
// 区间分组 将区间分成若干组
// 使得每组内部的区间两两之间(包括端点)没有交集
int range_fenzu(vector<pii> &range) {
int n = range.size();
sort(range.begin(), range.end(), [&](const pii &a, const pii &b) {
return a.fi < b.fi;
});
priority_queue<int, vector<int>, greater<int>> heap;
heap.push(range[0].se);
for(int i = 1; i<n; i++){
if(range[i].fi > heap.top())
heap.pop();
heap.push(range[i].se);
}
return heap.size();
}
// 区间覆盖线段[s,t]
// 选尽可能少的区间把线段完全覆盖
int range_fugai_segment(vector<pii> &range, int s, int t) {
int n = range.size();
sort(range.begin(), range.end(), [&](const pii &a, const pii &b) {
return a.fi < b.fi;
});
int st = s, res = 0;
bool flag = false;
for (int i=0; i<n; i++) {
int j = i, tmp=-2e9;
while(j < n && range[j].fi <= st) {
tmp = max(tmp, range[j].se);
j++;
}
if (tmp==-2e9) break;
res++;
st = tmp;
if(st >= t) {
flag = true;
break;
}
i = j - 1;
}
if (!flag) res = -1;
return res;
}
// RMQ 区间最大/小值
struct RMQ {
const int N = 1e5, M = 18;
vector<vector<int>> f;
RMQ(int n, vector<int> &a) {
for(int j=0; j<M; j++)
for(int i=1; i + (1 << j) - 1 <=n; i++) {
if(!j) f[i][j] = a[i];
else f[i][j] = max(f[i][j-1], f[i + (1 << (j-1))][j-1]);
}
}
int query(int l, int r) {
int len = r - l + 1;
int k = log(len) / log(2);
return max(f[l][k], f[r-(1<<k)+1][k]);
}
};
// DP LIS(最长递增子序列)
int LIS(int n, vector<int> &a) {
vector<int> f(n + 1), b(n + 1, 0);
SegmentTree tr; // 区间max线段树
tr.init(n, b);
int maxlen = 0;
for (int i=1; i<=n; i++) {
int t = tr.query(1, 1, a[i] - 1);
// printf("l i=%d find=%d t=%d\n", i, find(a[i]), t);
f[i] = t + 1;
tr.modify(1, a[i], t + 1);
maxlen = max(maxlen, t + 1);
}
return maxlen;
}
// 计算几何部分
// 1. 前置知识点
// (1) pi = acos(-1);
// (2) 余弦定理 c^2 = a^2 + b^2 - 2abcos(t)
// 2. 浮点数的比较
// const double eps = 1e-8;
int sgn(double x) { // 符号函数
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
return 1;
}
int cmp(double x, double y) { // 比较函数
if (fabs(x - y) < eps) return 0;
if (x < y) return -1;
return 1;
}
// 3. 向量
struct Point {
double x, y;
Point() {}
Point(double _x, double _y) : x(_x), y(_y) {}
Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); }
Point operator+(const Point &b) const { return Point(x + b.x, y + b.y); }
double operator^(const Point &b) const { return x * b.y - y * b.x; } //叉积
double operator*(const Point &b) const { return x * b.x + y * b.y; } //点积
bool operator<(const Point &b) const { return x < b.x || (x == b.x && y < b.y); }
bool operator==(const Point &b) const { return sgn(x - b.x) == 0 && sgn(y - b.y) == 0; }
Point Rotate(double B, Point P) { //绕着点P,逆时针旋转角度B(弧度)
Point tmp;
tmp.x = (x - P.x) * cos(B) - (y - P.y) * sin(B) + P.x;
tmp.y = (x - P.x) * sin(B) + (y - P.y) * cos(B) + P.y;
return tmp;
}
};
double dist(Point a, Point b) { return sqrt((a - b) * (a - b)); } //两点间距离
double len(Point a){return sqrt(a.x * a.x + a.y * a.y);}//向量的长度
// 3.1 向量的加减法和数乘运算
// 3.2 内积(点积) A·B = |A||B|cos(C)
// (1) 几何意义:向量A在向量B上的投影与B的长度的乘积。
// 3.3 外积(叉积) AxB = |A||B|sin(C)
// (1) 几何意义:向量A与B张成的平行四边形的有向面积。B在A的逆时针方向为正。
// 3.4 常用函数
// 3.4.1 取模
double get_length(Point a) {
return sqrt(a * a);
}
// 3.4.2 计算向量夹角
double get_angle(Point a, Point b) {
return acos((a * a) / get_length(a) / get_length(b));
}
// 3.4.3 计算两个向量构成的平行四边形有向面积
double area(Point a, Point b, Point c) {
return (b - a) ^ (c - a);
}
// 3.4.5 向量A顺时针旋转C的角度:
Point rotate(Point a, double angle) {
return Point(a.x * cos(angle) + a.y * sin(angle), -a.x * sin(angle) + a.y * cos(angle));
}
// 直线
struct Line {
Point s, e;
Line() {}
Line(Point _s, Point _e) : s(_s), e(_e) {}
//两直线相交求交点
//第一个值为0表示直线重合,为1表示平行,为2是相交
//只有第一个值为2时,交点才有意义
pair<int, Point> operator&(const Line &b) const {
Point res = s;
if (sgn((s - e) ^ (b.s - b.e)) == 0) {
if (sgn((s - b.e) ^ (b.s - b.e)) == 0)
return make_pair(0, res); //重合
else
return make_pair(1, res); //平行
}
double t = ((s - b.s) ^ (b.s - b.e)) / ((s - e) ^ (b.s - b.e));
res.x += (e.x - s.x) * t;
res.y += (e.y - s.y) * t;
return make_pair(2, res);
}
};
// 判断线段是否相交 返回1表示相交,0表示不相交
bool inter(Line l1, Line l2) {
return max(l1.s.x, l1.e.x) >= min(l2.s.x, l2.e.x) &&
max(l2.s.x, l2.e.x) >= min(l1.s.x, l1.e.x) &&
max(l1.s.y, l1.e.y) >= min(l2.s.y, l2.e.y) &&
max(l2.s.y, l2.e.y) >= min(l1.s.y, l1.e.y) &&
sgn((l2.s - l1.e) ^ (l1.s - l1.e)) * sgn((l2.e - l1.e) ^ (l1.s - l1.e)) <= 0 &&
sgn((l1.s - l2.e) ^ (l2.s - l2.e)) * sgn((l1.e - l2.e) ^ (l2.s - l2.e)) <= 0;
}
// 判断直线l1和线段l2是否相交 返回1表示相交,0表示不相交
bool Seg_inter_line(Line l1, Line l2) {
return sgn((l2.s - l1.e) ^ (l1.s - l1.e)) * sgn((l2.e - l1.e) ^ (l1.s - l1.e)) <= 0;
}
// 点到直线的距离 返回result(点到直线上最近的点,垂足)
Point PointToLine(Point P, Line L) {
Point result;
double t = ((P - L.s) * (L.e - L.s)) / ((L.e - L.s) * (L.e - L.s));
result.x = L.s.x + (L.e.x - L.s.x) * t;
result.y = L.s.y + (L.e.y - L.s.y) * t;
return result;
}
// 求点到线段的距离 返回点到线段上最近的点
Point NearestPointToLineSeg(Point P, Line L) {
Point result;
double t = ((P - L.s) * (L.e - L.s)) / ((L.e - L.s) * (L.e - L.s));
if (t >= 0 && t <= 1)
{
result.x = L.s.x + (L.e.x - L.s.x) * t;
result.y = L.s.y + (L.e.y - L.s.y) * t;
}
else
{
if (dist(P, L.s) < dist(P, L.e))
result = L.s;
else
result = L.e;
}
return result;
}
// 计算多边形面积,点的编号从0~n-1
double CalcArea(Point p[], int n) {
double res = 0;
for (int i = 0; i < n; i++)
res += (p[i] ^ p[(i + 1) % n]) / 2;
return fabs(res);
}
// 判断点在线段上
bool OnSeg(Point P, Line L) {
return sgn((L.s - P) ^ (L.e - P)) == 0 &&
sgn((P.x - L.s.x) * (P.x - L.e.x)) <= 0 &&
sgn((P.y - L.s.y) * (P.y - L.e.y)) <= 0;
}
// 求凸包Anderw算法
// p为点的编号0, 1, 2, ..., n-1. n为点的数量
// ch为生成的凸包上的点
// 返回凸包大小m, 编号0, ..., m-1
int ConvexHull(Point *p, int n, Point *ch) {//求凸包
sort(p, p + n);
n = unique(p, p + n) - p; //去重
int m = 0;
for (int i = 0; i < n; ++i) {
while (m > 1 && sgn((ch[m - 1] - ch[m - 2]) ^ (p[i] - ch[m - 1])) <= 0)
--m;
ch[m++] = p[i];
}
int k = m;
for (int i = n - 2; i >= 0; i--) {
while (m > k && sgn((ch[m - 1] - ch[m - 2]) ^ (p[i] - ch[m - 1])) <= 0)
--m;
ch[m++] = p[i];
}
if (n > 1)
m--;
return m;
}
/*
判断点在凸多边形内, 要求:
点形成一个凸包,而且按逆时针排序
如果是顺时针把里面的<0改为>0
点的编号:0~n-1
返回值:
-1:点在凸多边形外
0:点在凸多边形边界上
1:点在凸多边形内
*/
int inConvexPoly(Point a, Point p[], int n) {
for (int i = 0; i < n; i++)
{
if (sgn((p[i] - a) ^ (p[(i + 1) % n] - a)) < 0)
return -1;
else if (OnSeg(a, Line(p[i], p[(i + 1) % n])))
return 0;
}
return 1;
}
/*
判断点在任意多边形内
射线法,poly[]的顶点数要大于等于3,点的编号0~n-1
返回值
-1:点在凸多边形外
0:点在凸多边形边界上
1:点在凸多边形内
*/
int inPoly(Point p, Point poly[], int n) {
int cnt;
Line ray, side;
cnt = 0;
ray.s = p;
ray.e.y = p.y;
ray.e.x = -100000000000.0; //-INF,注意取值防止越界
for (int i = 0; i < n; i++) {
side.s = poly[i];
side.e = poly[(i + 1) % n];
if (OnSeg(p, side))
return 0;
//如果平行轴则不考虑
if (sgn(side.s.y - side.e.y) == 0) continue;
if (OnSeg(side.s, ray)) {
if (sgn(side.s.y - side.e.y) > 0)
cnt++;
}
else if (OnSeg(side.e, ray)) {
if (sgn(side.e.y - side.s.y) > 0)
cnt++;
}
else if (inter(ray, side))
cnt++;
}
if (cnt % 2 == 1)
return 1;
else
return -1;
}
void solve() {
}
int main() {
// std::ios::sync_with_stdio(false);
// std::cin.tie(nullptr);
// std::cout.tie(nullptr);
// multiple case
// int t; scanf("%d", &t);
// while(t--) {
// solve();
// }
// single case
solve();
return 0;
}
不忘初心方得始终