个人算法竞赛模板(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;
}
posted @ 2024-05-04 14:41  1v7w  阅读(48)  评论(0编辑  收藏  举报