CP Template

Default

// Skyqwq
#include <bits/stdc++.h>

#define pb push_back
#define fi first
#define se second
#define mp make_pair

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }

template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

int main() {
    
    return 0;
}

图论

Hall 定理:

完美匹配:集合 $\le$ 邻域的并

最大匹配:总数 - $\max(集合 -邻域并)$

矩阵树定理:

度数 - 边

欧拉回路计数:

Best 定理

内向生成树个数 $\prod (out_i-1)$

// 圆方树

int dfn[N], low[N], dfncnt, cnt;
 
int s[N], top;
 
void inline Add(int x, int y) {
    g[x].pb(y), g[y].pb(x);
}
 
void tarjan(int u, int fa) {
    dfn[u] = low[u] = ++dfncnt;
    s[++top] = u;
    for (int v: e[u]) {
        if (v == fa) continue;
        if (!dfn[v]) {
            tarjan(v, u);
            chkMin(low[u], low[v]);
            if (low[v] >= dfn[u]) {
                int y; ++cnt;
                do {
                    y = s[top--], Add(y, cnt);
                } while (y != v);
                Add(cnt, u);
            }
        } else {
            chkMin(low[u], dfn[v]);
        }
    }
}

// 有向图 tarjan

void tarjan(int u) {
    dfn[u] = low[u] = ++dfncnt;
    s[++top] = u, ins[u] = true;
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].v;
        if (!dfn[v]) {
            tarjan(v), low[u] = min(low[u], low[v]);
        } else if (ins[v]) low[u] = min(low[u], dfn[v]);
    }
    if (low[u] == dfn[u]) {
        int v; ++cnt;
        do {
            v = s[top--], ins[v] = false, col[v] = cnt;
        } while (v != u);
    }
}

// 欧拉回路
void dfs(int u) {
    for (int &i = head[u]; i; ) {
        int v = e[i].v;
        if(vis[i]) {
            i = e[i].next;
            continue;
        }
        
        vis[i] = true;
        if(t == 1) vis[i ^ 1] = true;
        
        i = e[i].next;
        dfs(v);
    }
}

// 最大流
namespace MF{
    int n, m, s, t, pre[N], cur[N], q[N];
    LL res, maxflow, d[N];
    int head[N], numE = 1;
    struct E{
        int next, v, w;
    } e[M << 1];
    
    void inline add(int u, int v, int w) {
        e[++numE] = (E) { head[u], v, w };
        head[u] = numE;
    }
    void inline init(int v, int a, int b) {
        for (int i = 1; i <= n; i++) head[i] = 0;
        numE = 1;
        n = v, s = a, t = b;
    }
    
    bool inline bfs() {
        int hh = 0, tt = -1;
        for (int i = 1; i <= n; i++) d[i] = 0;
        q[++tt] = s, d[s] = 1, cur[s] = head[s];
        while (hh <= tt) {
            int u = q[hh++];
            for (int i = head[u]; i; i = e[i].next) {
                int v = e[i].v;
                if (!d[v] && e[i].w) {
                    cur[v] = head[v];
                    q[++tt]= v, d[v] = d[u] + 1;
                    if (v == t) return 1;
                }
            }
        }
        return 0;
    }
    LL dinic(int u, LL flow) {
        if (u == t) return flow;
        LL rest = flow;
        for (int i = cur[u]; i && rest; i = e[i].next) {
            cur[u] = i;
            int v = e[i].v;
            if (e[i].w&& d[v] == d[u] + 1) {
                int k = dinic(v, min((LL)e[i].w, rest));
                if (!k) d[v] = 0;
                rest -= k, e[i].w -= k,e[i ^ 1].w += k;
            }
        }
        return flow - rest;
    }
    void inline addE(int u, int v, int w) {
        add(u, v, w), add(v, u, 0);
    }
    LL inline work() {
        maxflow = 0;
        while (bfs())
            while (res = dinic(s, INF)) maxflow += res;
        return maxflow; 
    }
    // Find min-cut 
    bool vis[N];
    
    void dfs(int u) {
        //cerr << u << " dfs\n";
        vis[u] = 1;
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].v;
            if (!vis[v] && e[i].w) dfs(v);
        }
    }
    
    void minCut() {
        for (int i = 1; i <= n; i++) vis[i] = 0;
        dfs(s);
    }
}
// Prufer
void inline fToP() {
	for (int i = 1; i < n; i++) d[f[i]]++;
	for (int i = 1, j = 1; i <= n - 2; j++) {
		while (d[j]) j++;
		p[i++] = f[j];
		while (i <= n - 2 && --d[p[i - 1]] == 0 && p[i - 1] < j) p[i++] = f[p[i - 1]];
	}
}

void inline pToF() {
    for (int i = 1; i <= n - 2; i++) d[p[i]]++;
    p[n - 1] = n;
    for (int i = 1, j = 1; i < n; i++, j++) {
    	while (d[j]) j++;
    	f[j] = p[i];
    	while (i < n - 1 && --d[p[i]] == 0 && p[i] < j) f[p[i]] = p[i + 1], ++i;
    }
}

// Start :最小树形图
int rt = 1, col, in[N];
int vis[N], id[N], pre[N];
struct E{
    int u, v, w;
} e[M];
int inline edmonds() {
    int ans = 0;
    while (true) {
        for (int i = 1; i <= n; i++) in[i] = INF;
        memset(vis, 0, sizeof vis);
        memset(id, 0, sizeof id);
        for (int i = 1; i <= m; i++) 
            if (e[i].w < in[e[i].v]) in[e[i].v] = e[i].w, pre[e[i].v] = e[i].u;
        for (int i = 1; i <= n; i++)
            if (in[i] == INF && i != rt) return -1;
        col = 0;
        for (int i = 1; i <= n; i++) {
            if (i == rt) continue;
            ans += in[i];
            int v = i;
            while (!vis[v] && !id[v] && v != rt)
                vis[v] = i, v = pre[v];
            if (v != rt && vis[v] == i) {
                id[v] = ++col;
                for (int x = pre[v]; x != v; x = pre[x]) id[x] = col;
            }
        }
        if (!col) break;
        for (int i = 1; i <= n; i++) if (!id[i]) id[i] = ++col;
        int tot = 0;
        for (int i = 1; i <= m; i++) {
            int a = id[e[i].u], b = id[e[i].v];
            if (a == b) continue;
            e[++tot] = (E) { a, b, e[i].w - in[e[i].v] };
        }
        m = tot, n = col, rt = id[rt];
    }
    return ans;
}

// Start : 长链剖分 + O(1) k 级祖先

int d[N], dep[N];
int g[N], son[N], fa[N][L], top[N];
LL res;
vector<int> U[N], D[N];
void dfs1(int u) {
    dep[u] = d[u] = d[fa[u][0]] + 1;
    for (int i = 1; fa[u][i - 1]; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].v;
        dfs1(v);
        if (dep[v] > dep[u]) dep[u] = dep[v], son[u] = v;
    }
} 

void dfs2(int u, int tp) {
    top[u] = tp;
    if (u == tp) {
        for (int x = u, i = 0; i <= dep[u] - d[u]; i++)
            U[u].push_back(x), x = fa[x][0];
        for (int x = u, i = 0; i <= dep[u] - d[u]; i++)
            D[u].push_back(x), x = son[x];
    }
    if (son[u]) dfs2(son[u], tp);
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].v;
        if (v != son[u]) dfs2(v, v);
    }
}

int inline query(int x, int k) {
    if (!k) return x;
    x = fa[x][g[k]], k -= (1 << g[k]) + d[x] - d[top[x]], x = top[x];
    return k < 0 ? D[x][-k] : U[x][k];
}

// --End
// 最小费用最大流 EK

const int N = ?, M = ?;
const int INF = 0x3f3f3f3f;
int n, m, s, t, maxflow, cost, d[N], incf[N], pre[N];
int q[N];
int head[N], numE = 1;

bool vis[N];

struct E{
    int next, v, w, c;
} e[M];

void inline add(int u, int v, int w, int c) {
    e[++numE] = (E) { head[u], v, w, c };
    head[u] = numE;
}

// Spfa || 
bool spfa() {
    memset(vis, false, sizeof vis);
    memset(d, 0x3f, sizeof d);
    int hh = 0, tt = 1;
    q[0] = s; d[s] = 0; incf[s] = 2e9;
    while (hh != tt) {
        int u = q[hh++]; vis[u] = false;
        if (hh == N) hh = 0;
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].v;
            if (e[i].w && d[u] + e[i].c < d[v]) {
                d[v] = d[u] + e[i].c;
                pre[v] = i;
                incf[v] = min(incf[u], e[i].w);
                if (!vis[v]) {
                    q[tt++] = v;
                    vis[v] = true;
                    if (tt == N) tt = 0;
                }
            }
        }
    } 
    return d[t] != INF;
}

void update() {
    int x = t;
    while (x != s) {
        int i = pre[x];
        e[i].w -= incf[t], e[i ^ 1].w += incf[t];
        x = e[i ^ 1].v;
    }
    maxflow += incf[t];
    cost += d[t] * incf[t];
}

// --End

namespace KM{
    int n, va[N], vb[N], match[N], last[N];
    LL a[N], b[N], upd[N], w[N][N];
    bool dfs(int u, int fa) {
        va[u] = 1;
        for (int v = 1; v <= n; v++) {
            if (vb[v]) continue;
            if (a[u] + b[v] == w[u][v]) {
                vb[v] = 1, last[v] = fa;
                if (!match[v] || dfs(match[v], v)) {
                    match[v] = u; return true;
                }
            } else if (a[u] + b[v] - w[u][v] < upd[v])
                upd[v] = a[u] + b[v] - w[u][v], last[v] = fa;
        }
        return false;
    }
    void inline calc(int len, LL d[N][N]) {
        n = len;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++) w[i][j] = d[i][j];
        for (int i = 1; i <= n; i++) {
            a[i] = -1e18, b[i] = 0;
            for (int j = 1; j <= n; j++)
                a[i] = max(a[i], w[i][j]);
        }
        for (int i = 1; i <= n; i++) {
            memset(va, 0, sizeof va);
            memset(vb, 0, sizeof vb);
            memset(upd, 0x3f, sizeof upd);
            int st = 0; match[0] = i;
            while (match[st]) {
                LL delta = 1e18;
                if (dfs(match[st], st)) break;
                for (int j = 1; j <= n; j++) {
                    if (!vb[j] && upd[j] < delta) 
                        delta = upd[j], st = j;
                }
                for (int j = 1; j <= n; j++) {
                    if (va[j]) a[j] -= delta;
                    if (vb[j]) b[j] += delta;
                    else upd[j] -= delta;
                }
                vb[st] = true;
            }
            while (st) {
                match[st] = match[last[st]];
                st = last[st];
            }
        }
    }
}



// 有负圈 / 上下界
struct MCMF2{
    const int N = 205, M = 10005;
    const int INF = 0x3f3f3f3f;
    int n, m, s, t, maxflow, cost, d[N], incf[N], pre[N];
    int q[N], in, S, T;
    int head[N], a[N], numE = 1, a0, a1;
    bool vis[N];
    struct E{
        int next, v, w, c;
    } e[M << 2];
    void inline add(int u, int v, int w, int c) {
        e[++numE] = (E) { head[u], v, w, c };
        head[u] = numE;
    }
    void inline addE(int u, int v, int w, int c) {
        add(u, v, w, c), add(v, u, 0, -c);
    }
    bool spfa() {
        memset(vis, false, sizeof vis);
        memset(d, 0x3f, sizeof d);
        int hh = 0, tt = 1;
        q[0] = S; d[S] = 0; incf[S] = 2e9;
        while (hh != tt) {
            int u = q[hh++]; vis[u] = false;
            if (hh == N) hh = 0;
            for (int i = head[u]; i; i = e[i].next) {
                int v = e[i].v;
                if (e[i].w && d[u] + e[i].c < d[v]) {
                    d[v] = d[u] + e[i].c;
                    pre[v] = i;
                    incf[v] = min(incf[u], e[i].w);
                    if (!vis[v]) {
                        q[tt++] = v;
                        vis[v] = true;
                        if (tt == N) tt = 0;
                    }
                }
            }
        } 
        return d[T] != INF;
    }
    void update() {
        int x = T;
        while (x != S) {
            int i = pre[x];
            e[i].w -= incf[T], e[i ^ 1].w += incf[T];
            x = e[i ^ 1].v;
        }
        maxflow += incf[T];
        cost += d[T] * incf[T];
    }

    void inline addEdge(int u, int v, int l, int d, int c) {
        a[v] += l, a[u] -= l;
        addE(u, v, d - l, c);
    }

    void inline work() {
        while (spfa()) update();
    }

    void inline ADD(int u, int v, int w, int c) {
        if (c >= 0) addEdge(u, v, 0, w, c); 
        else a[v] += w, a[u] -= w, addEdge(v, u, 0, w, -c), a1 += c * w;
    }

    void inline solve() {
        for (int i = 1; i <= n; i++) {
            if (!a[i]) continue;
            if (a[i] > 0) addEdge(S, i, 0, a[i], 0);
            else addEdge(i, T, 0, -a[i], 0);
        }
        addEdge(T, S, 0, INF, 0);
        work();
        S = s, T = t;
        a1 += cost;
        maxflow = cost = 0;
        e[numE].w = e[numE - 1].w = 0;
        work();
        a0 += maxflow, a1 += cost;
    }
}

// 虚树

void insert(int x) {
    if (!top) { s[++top] = x; return; }
    int p = lca(x, s[top]);
    while (top > 1 && dep[s[top - 1]] >= dep[p]) e[s[top - 1]].pb(s[top]), top--;
    if (s[top] != p) {
        e[p].pb(s[top]);
        s[top] = p;
    }
    s[++top] = x;
}


bool inline cmp(int x, int y) {
    return dfn[x] < dfn[y];
}
int inline build(vector<int> &A) {
    top = 0;
    sort(A.begin(), A.end(), cmp);
    for (int x: A) {
        insert(x);
    }
    for (int i = 1; i < top; i++)
        e[s[i]].pb(s[i + 1]);
    return s[1];
}

Poly

// 1e18 多项式乘法》。。。别用fft(mtt也不会写

#define I __int128_t
typedef vector<I> Poly;
const I P = 1945555039024054273ll, G = 5;
// p=1945555039024054273=27\times 2^{56}+1,g=5
 
 
I A[N], rev[N];
I lim = 1, len = 0;
LL W[19][N];
 
I inline power(I a, I b, I Mod = P) {
	I res = 1;
	while (b) {
		if (b & 1) res = res * a % Mod;
		a = a * a % Mod;
		b >>= 1;
	}
	return res;
}
 
 
void inline NTT(I c[], int lim, int o) {
	for (int i = 0; i < lim; i++)
		if (i < rev[i]) swap(c[i], c[rev[i]]);
	for (int k = 1, t = 0; k < lim; k <<= 1, t++) {
		for (int i = 0; i < lim; i += (k << 1)) {
			for (int j = 0; j < k; j++) {
				I u = c[i + j], v = (I)c[i + k + j] * W[t][j] % P;
				c[i + j] = u + v >= P ? u + v - P : u + v;
				c[i + j + k] = u - v < 0 ? u - v + P : u - v; 
			}
		}
	}
	if (o == -1) {
		reverse(c + 1, c + lim);
		I inv = power(lim, P - 2, P);
		for (int i = 0; i < lim; i++)
			c[i] = c[i] * inv % P;
	}
}
 
void inline setN(int n) {
	lim = 1, len = 0;
	while (lim < n) lim <<= 1, len++;
	for (int i = 0; i < lim; i++)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (len - 1));
}
 
Poly inline NTT(Poly a, int o) {
	int n = a.size();
	for (int i = 0; i < n; i++) A[i] = a[i];
	NTT(A, lim, o);
	a.clear();
	for (int i = 0; i < lim; i++) a.push_back(A[i]), A[i] = 0;
	return a;
}
 
Poly inline mul (Poly a, Poly b, int newn = -1) {
	if (newn == -1) newn = a.size() + b.size() - 1;
	setN(a.size() + b.size() - 1);
	Poly c = NTT(a, 1), d = NTT(b, 1);
	for (int i = 0; i < lim; i++) c[i] = (I)c[i] * d[i] % P;
	d = NTT(c, -1); d.resize(newn);
	return d;
}
 
// 用到的最大的 n
void inline init(int n) {
	setN(n);
	for (int k = 1, t = 0; k < lim; k <<= 1, t++) {
		I wn = power(G, (P - 1) / (k << 1));
		W[t][0] = 1;
		for (int j = 1; j < k; j++) W[t][j] = (I)W[t][j - 1] * wn % P;
	}
}

// -- 
typedef vector<int> Poly;

#define pb push_back

const int N = 8e5 + 5, P = 998244353, G = 3;

int A[N], rev[N], mod, inv[N], fact[N], infact[N];
int lim = 1, len = 0, W[20][N];

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

int Gi = power(G, P - 2, P), inv2 = power(2, P - 2, P);

void inline NTT(int c[], int lim, int o) {
	for (int i = 0; i < lim; i++)
		if (i < rev[i]) swap(c[i], c[rev[i]]);
	for (int k = 1, t = 0; k < lim; k <<= 1, t++) {
		for (int i = 0; i < lim; i += (k << 1)) {
			for (int j = 0; j < k; j++) {
				int u = c[i + j], v = (LL)c[i + k + j] * W[t][j] % P;
				c[i + j] = u + v >= P ? u + v - P : u + v;
				c[i + j + k] = u - v < 0 ? u - v + P : u - v; 
			}
		}
	}
	if (o == -1) {
		reverse(c + 1, c + lim);
		int inv = power(lim, P - 2, P);
		for (int i = 0; i < lim; i++)
			c[i] = (LL)c[i] * inv % P;
	}
}

void inline setN(int n) {
	lim = 1, len = 0;
	while (lim < n) lim <<= 1, len++;
	for (int i = 0; i < lim; i++)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (len - 1));
}

Poly inline NTT(Poly a, int o) {
	int n = a.size();
	for (int i = 0; i < n; i++) A[i] = a[i];
	NTT(A, lim, o);
	a.clear();
	for (int i = 0; i < lim; i++) a.push_back(A[i]), A[i] = 0;
	return a;
}

Poly inline mul (Poly a, Poly b, int newn = -1) {
	if (newn == -1) newn = a.size() + b.size() - 1;
	setN(a.size() + b.size() - 1);
	Poly c = NTT(a, 1), d = NTT(b, 1);
	for (int i = 0; i < lim; i++) c[i] = (LL)c[i] * d[i] % P;
	d = NTT(c, -1); d.resize(newn);
	return d;
}

// 用到的最大的 n
void inline init(int n) {
	setN(2 * n);
	for (int k = 1, t = 0; k < lim; k <<= 1, t++) {
		int wn = power(G, (P - 1) / (k << 1));
		W[t][0] = 1;
		for (int j = 1; j < k; j++) W[t][j] = (LL)W[t][j - 1] * wn % P;
	}
}

// f[0 ... n] 线性递推第 b 项
// g[1 ~ k] 为递推多项式

int inline LRS(int b, Poly f, Poly g) {
	int k = g.size() - 1;
	g[0] = 1;
	for (int i = 1; i <= k; i++) g[i] = (P - g[i]) % P;
	Poly h = mul(f, g, k);
	while (b) {
		Poly g2 = g;
		for (int i = 0; i < g2.size(); i += 2)
			g2[i] = (P - g2[i]) % P;
		Poly t = mul(g2, g); g.clear();
		for (int i = 0; i < t.size(); i += 2)
			g.pb(t[i]);
		t = mul(g2, h); h.clear();
		for (int i = (b & 1); i < t.size(); i += 2)
			h.pb(t[i]);
		b >>= 1;
	}
	return (LL)h[0] * power(g[0], P - 2) % P;
}

字符串

struct ACAutomation{
	int tr[SZ][26], nxt[SZ], idx, q[SZ];
	void inline insert(char s[]) {
		int p = 0;
		for (int j = 0; s[j]; j++) {
			int ch = s[j] - 'a';
			if(!tr[p][ch]) tr[p][ch] = ++idx;
			p = tr[p][ch];
		}
	}
	void build() {
		int hh = 0, tt = -1;
		for (int i = 0; i < 26; i++) 
			if (tr[0][i]) q[++tt] = tr[0][i];
		while (hh <= tt) {
			int u = q[hh++];
			for (int i = 0; i < 26; i++) {
				int v = tr[u][i];
				if (!v) tr[u][i] = tr[nxt[u]][i];
				else nxt[v] = tr[nxt[u]][i], q[++tt] = v;
			}
		}
	}
}


void manacher() {
	int r = 0, mid = 0;
	for (int i = 1; i <= n; i++) {
		p[i] = i <= r ? min(r - i + 1, p[2 * mid - i]) : 1;
		while (g[i - p[i]] == g[i + p[i]]) ++p[i];
		if (i + p[i] - 1 > r) mid = i, r = i + p[i] - 1;
		ans = max(ans, p[i] - 1);
	}
}

struct SA{
	int rk[SZ], sa[SZ], cnt[SZ], oldrk[SZ], id[SZ], n, m, p, height[SZ];
	bool inline cmp(int i, int j, int k) {
		return oldrk[i] == oldrk[j] && oldrk[i + k] == oldrk[j + k];
	}
	void inline build(char s[]) {
		n = strlen(s + 1), m = 221;
		for (int i = 1; i <= n; i++) cnt[rk[i] = s[i]]++;
		for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
		for (int i = n; i; i--) sa[cnt[rk[i]]--] = i;
		for (int w = 1; w < n; w <<= 1, m = p) {
			p = 0;
			for (int i = n; i > n - w; i--) id[++p] = i;
			for (int i = 1; i <= n; i++)
				if (sa[i] > w) id[++p] = sa[i] - w;
			for (int i = 1; i <= m; i++) cnt[i] = 0;
			for (int i = 1; i <= n; i++) cnt[rk[i]]++, oldrk[i] = rk[i];
			for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
			for (int i = n; i; i--) sa[cnt[rk[id[i]]]--] = id[i];
			p = 0;
			for (int i = 1; i <= n; i++) {
				rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;
			}
			if (p == n) break;
		}
		for (int i = 1; i <= n; i++) {
			int j = sa[rk[i] - 1], k = max(0, height[rk[i - 1]] - 1);
			while (s[i + k] == s[j + k]) k++;
			height[rk[i]] = k;
		}
	}
};

// 切记复制一倍到后面, 最小表示法,返回开始下标
int inline minExp(int a[], int n) {
	int i = 1, j = 2;
	while (i <= n && j <= n) {
		int k;
		for (k = 0; k < n && a[i + k] == a[j + k]; k++);
		if (k == n) break;
		if (a[i + k] < a[j + k]) j += k + 1;
		else i += k + 1;
		if (i == j) i++;
	}
	return min(i, j);
}
// Z 函数

z[1] = n;
for (int i = 2, r = 0, j = 0; i <= n; i++) {
	if (i <= r) z[i] = min(r - i + 1, z[i - j + 1]);
	while (i + z[i] <= n && a[i + z[i]] == a[1 + z[i]]) z[i]++;
	if (i + z[i] - 1 > r) r = i + z[i] - 1, j = i; 
}

for (int i = 1, r = 0, j = 0; i <= m; i++) {
	if (i <= r) p[i] = min(r - i + 1, z[i - j + 1]);
	while (i + p[i] <= m && b[i + p[i]] == a[1 + p[i]]) p[i]++;
	if (i + p[i] - 1 > r) r = i + p[i] - 1, j = i; 
}

struct SAM{
	int idx, last;
	struct SAM_{
		int nxt[26], len, link;
	} t[N];
	void inline init() {
		last = idx = 1;
	}
	
	void inline extend(int c) {
		int x = ++idx, p = last; sz[x] = 1;
		t[x].len = t[last].len + 1;
		while (p && !t[p].nxt[c]) 
			t[p].nxt[c] = x, p = t[p].link;
		if (!p) t[x].link = 1;
		else {
			int q = t[p].nxt[c];
			if (t[p].len + 1 == t[q].len) t[x].link = q;
			else {
				int y = ++idx;
				t[y] = t[q], t[y].len = t[p].len + 1;
				while (p && t[p].nxt[c] == q)
					t[p].nxt[c] = y, p = t[p].link;
				t[q].link = t[x].link = y;
			}
		}
		last = x;
	}
} t;

struct GSAM{
	int idx, last;
	struct SAM{
		int ch[26], len, link;
	} t[N];
	void inline init() {
		last = idx = 1;
	}
	void inline insert(int c) {
		int p = last;
		if (t[p].ch[c]) {
			int q = t[p].ch[c];
			if (t[q].len == t[p].len + 1) last = q;
			else {
				int y = ++idx; t[y] = t[q];
				t[y].len = t[p].len + 1;
				while (p && t[p].ch[c] == q)
					t[p].ch[c] = y, p = t[p].link;
				t[q].link = y;
				last = y;	
			}
			return;
		}
		int x = ++idx; t[x].len = t[p].len + 1;
		while (p && !t[p].ch[c]) t[p].ch[c] = x, p = t[p].link;
		int q, y;
		if (!p) t[x].link = 1;
		else {
			q = t[p].ch[c];
			if (t[q].len == t[p].len + 1) t[x].link = q;
			else {
				int y = ++idx; t[y] = t[q];
				t[y].len = t[p].len + 1;
				while (p && t[p].ch[c] == q)
					t[p].ch[c] = y, p = t[p].link;
				t[q].link = t[x].link = y;
				last = y;	
			}
		}
		last = x;
	}
} t;

// 回文自动机
struct PAM{
	int n, ch[SZ][26], fail[SZ], len[SZ], sz[SZ], idx = -1, lastans, last;

	char s[SZ];

	int inline newNode(int x) {	len[++idx] = x; return idx; }
	int inline getFail(int x) {
		while (s[n - len[x] - 1] != s[n]) x = fail[x];
		return x;
	}

	int inline insert(char c) {
		int k = c - 'a';
		s[++n] = c;
		int p = getFail(last), x;
		if (!ch[p][k]) {
			x = newNode(len[p] + 2);
			fail[x] = ch[getFail(fail[p])][k];
			ch[p][k] = x, sz[x] = 1 + sz[fail[x]];
		} else x = ch[p][k];
		last = x;
		return sz[x];
	}

	void inline build() {
		newNode(0), newNode(-1);
		s[0] = '$', fail[0] = 1, last = 0;
	}
}

Math

单位根反演:

$$[n | k] = \frac{1}{n}\displaystyle \sum_{i=1}^{n-1}w_n^{ik}$$

常见积分表:

// 扩域
struct C{
    int x, y;
    // x + y * sqrt(o);
};
 
int o = 2;
 
// fn = Aa^n + Bb^n
 
int inline power(int a, int b) {
    int ret = 1;
    while (b) {
        if (b & 1) ret = 1ll * ret * a % P;
        a = 1ll * a * a % P;
        b >>= 1;
    }
    return ret;
}
 
 
 
 
int mod(int x) {
    return x >= P ? x - P : x;
}
 
C operator + (const C &a, const C &b) {
    return (C) { mod(a.x + b.x), mod(a.y + b.y) };
};
 
C operator * (const C &a, const C &b) {
    C c;
    c.x = (1ll * a.x * b.x + 1ll * a.y * b.y % P * o) % P;
    c.y = (1ll * a.x * b.y + 1ll * a.y * b.x) % P;
    return c;
};
 
C operator * (const C &a, const int &b) {
    C c;
    c.x = 1ll * a.x * b % P;
    c.y = 1ll * a.y * b % P;
    
    return c;
};
 
 
C inline power(C a, int b) {
    C ret = (C) { 1, 0 } ;
    while (b) {
        if (b & 1) ret = ret * a;
        a = a * a;
        b >>= 1;
    }
    return ret;
}
 
C operator / (const C &a, const C &b) {
    C c, d;
    c = a;
    d = b;
    d.y = mod(P - d.y);
    c = c * d;
    int I = (((LL)b.x * b.x - (LL)b.y * b.y * o) % P + P) % P;
    I = power(I, P - 2);
    c = c * I;
    return c;
};


// 原根 / 封装不太好


int n, D, phi[N], primes[N], tot, d[N], len;
int ans[N], cnt;

bool st[N], pr[N];

void inline init() {
	phi[1] = 1, pr[2] = pr[4] = true;
	for (int i = 2; i < N; i++) {
		if (!st[i]) primes[tot++] = i, phi[i] = i - 1;
		for (int j = 0; i * primes[j] < N; j++) {
			st[i * primes[j]] = true;
			if (i % primes[j] == 0) {
				phi[i * primes[j]] = phi[i] * primes[j];
				break;
			}
			phi[i * primes[j]] = phi[i] * (primes[j] - 1);
		}
	}
	for (int i = 1; i < tot; i++) {
		for (LL j = primes[i]; j < N; j *= primes[i]) pr[j] = true;
		for (LL j = 2 * primes[i]; j < N; j *= primes[i]) pr[j] = true;
	}
}


void inline factor(int m) {
	len = 0;
	for (int i = 0; i < tot && primes[i] * primes[i] <= m; i++) {
		int j = primes[i];
		if (m % j == 0) {
			d[len++] = j;
			while (m % j == 0) m /= j;
		}
	}
	if (m > 1) d[len++] = m;
}

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

bool inline check(int x, int P) {
	if (power(x, phi[P], P) != 1) return false;
	for (int i = 0; i < len; i++)
		if(power(x, phi[P] / d[i], P) == 1) return false;
	return true;
}

// 输入 P,返回最小原根

int inline get(int P) {
	for (int i = 1; i < P; i++) 
		if (check(i, P)) return i;
	return 0;
}
//-

void inline preInv(int n) {
    inv[1] = 1;
    for (int i = 2; i <= n; i++)
        inv[i] = ((LL)P - P / i) * inv[P % i] % P;
}


LL inline exgcd(LL a, LL b, LL &x, LL &y) {
	if (b == 0) {
		x = 1, y = 0;
		return a;
	}
	LL d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}


// 扩展中国剩余定理 exCRT
typedef pair<LL, LL> PLL;

LL gcd(LL a, LL b) {
	return b ? gcd(b, a % b) : a;
}

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;
}

LL mul(LL x, LL y, LL P) {
	return (__int128)x * y % P;
//	return x * y % P;
}


// x mod m = a (m1, a1) (m2, a2) return x


PLL inline merge(PLL A, PLL B) {
	LL a1 = A.fi, b1 = A.se;
	LL a2 = B.fi, b2 = B.se;
	LL a = a1 / gcd(a1, a2) * a2;
	LL x, y;
	LL d = exgcd(a1, a2, x, y);
	assert((b2 - b1) % d == 0);
	x = mul(x, (b2 - b1) / d, a);
	if (x < 0) x += a;
	LL o = mul(x, a1, a) + b1;
	if (o >= a) o -= a;
	PLL c = mp(a, o);
	return c;
}

// BSGS

unordered_map<int, int> mp;

int BSGS(int a, int b, int P) {
	int t = sqrt(P) + 1; mp.clear(); b %= P;
	for (int j = 0, s = b; j < t; j++) 
		mp[s] = j, s = (LL)s * a % P;
	a = power(a, t, P);
	for (int i = 1, s = 1; i <= t; i++) {
		s = (LL)s * a % P;
		if (mp.count(s) && i * t - mp[s] >= 0)
			return i * t - mp[s];
	}
	return -1;
}

int exBSGS(int a, int b, int P) {
	int x, y, d, A = 1, k = 0;
	while ((d = gcd(a, P)) > 1) {
		if (b % d) return -1;
		b /= d, P /= d, k++, A = (LL)A * (a / d) % P;
		if (A == b) return k;
	}
	exgcd(A, P, x, y); x = (x % P + P) % P;
	int res = BSGS(a, (LL)b * x % P, P);
	return res == -1 ? -1 : res + k;
}

const int N = 5000005, S = 3000;
const LL INF = 9e18;

LL p1[N], p2[S], m1[N], m2[S];

int n, primes[N], tot;

bool vis[N];

// 杜教筛 phi
LL s1(int x) {
	if (x < N) return p1[x];
	else if (p2[n / x] != INF) return p2[n / x]; 
	LL res = x * (x + 1ll) / 2;
	for (LL l = 2, r; l <= x; l = r + 1) {
		r = x / (x / l);
		res -= (r - l + 1) * s1(x / l);
	}
	return p2[n / x] = res;
}

// 杜教筛 mu

LL s2(int x) {
	if (x < N) return m1[x];
	else if (m2[n / x] != INF) return m2[n / x]; 
	LL res = 1;
	for (LL l = 2, r; l <= x; l = r + 1) {
		r = x / (x / l);
		res -= (r - l + 1) * s2(x / l);
	}
	return m2[n / x] = res;
}

// Min25

int inv2 = power(2, P - 2), inv6 = power(6, P - 2);

// 求 g_k 函数: <= x 的和
int inline getS(LL x, int k) {
	if (k == 1) return (x % P * (x % P + 1ll) % P * inv2 + P - 1ll)  % P;
	if (k == 2) return (P - 1ll + x % P * (x % P + 1ll) % P * (2ll * x % P + 1) % P * inv6) % P;
}

int inline getV(LL x, int k) {
	if (k == 1) return x % P;
	if (k == 2) return (LL)x % P * x % P;
}

bool vis[M];

int primes[M], tot;

void inline linear(int n) {
	for (int i = 2; i <= n; i++) {
		if (!vis[i]) primes[++tot] = i;
		for (int j = 1; primes[j] <= n / i; j++) {
			vis[i * primes[j]] = true;
			if (i % primes[j] == 0) break;
		}
	}
}

// 预处理 g_k 处所有 n / i 形式的质数前缀和

struct MP1{
	int m, g[M], pos1[M], pos2[M], len, id;
	LL n, d[M];
	int inline getPos(LL x) {
		return x <= m ? pos1[x] : pos2[n / x];
	}
	void inline add(LL v) {
		d[++len] = v;
		g[len] = getS(v, id);
		if (v <= m) pos1[v] = len;
		else pos2[n / v] = len; 
	}
	void build(LL sum, int t) {
		m = sqrt(n = sum); id = t;
		for (LL i = 1, j; i <= n; i = j + 1) {
			LL v = n / i; j = n / v;
			if (v <= m) break;
			add(v);
		}
		for (int i = m; i; i--) add(i);
		for (int i = 1; i <= tot && (LL)primes[i] * primes[i] <= n; i++) {
			LL pr = primes[i];
			for (int j = 1; j <= len && pr * pr <= d[j]; j++) {
				int k = getPos(d[j] / pr);
				g[j] = (g[j] - (LL)getV(pr, id) * (g[k] - g[getPos(primes[i - 1])] + P) % P + P) % P;
			}
		}
	}
	int inline s(LL x) { return g[getPos(x)]; }
} t1, t2;

int inline get(LL x) {
	return (t2.s(x) - t1.s(x) + P) % P;
}

int inline calc(LL x) {
	return x % P * (x % P - 1ll + P) % P;
}

void inline add(int &x, int y) {
	(x += y) %= P;
}

int inline s(LL n, int t) {
	if (primes[t] >= n) return 0;
	int ans = (get(n) - get(primes[t]) + P) % P;
	for (int i = t + 1; i <= tot && (LL)primes[i] * primes[i] <= n; i++) {
		int pr = primes[i];
		LL v = pr;
		for (int j = 1; v <= n; v = v * pr, j++) {
			add(ans, (LL)calc(v) * ((j != 1) + s(n / v, i)) % P);
		}
	}
	return ans;
}

// FMT / FWT

void inline OR(int n, int a[], int o) {
	for (int w = 1; w < n; w <<= 1) 
		for (int i = 0; i < n; i += (w << 1)) 
			for (int j = 0; j < w; j++)
				add(a[i + j + w], o * a[i + j]);
}

void inline AND(int n, int a[], int o) {
	for (int w = 1; w < n; w <<= 1) 
		for (int i = 0; i < n; i += (w << 1)) 
			for (int j = 0; j < w; j++)
				add(a[i + j], o * a[i + j + w]);
}


// 反向传 1/2
void inline XOR(int n, int a[], int o) {
	for (int w = 1; w < n; w <<= 1) 
		for (int i = 0; i < n; i += (w << 1)) 
			for (int j = 0; j < w; j++) {
				int u = a[i + j], v = a[i + j + w];
				a[i + j] = ((LL)u + v + P) * o % P;
				a[i + j + w] = ((LL)u - v + P) * o % P;
			}
}

// 子集卷积

void inline SubConv(int n, int a[], int b[], int c[]) {
	for (int i = 0; i < (1 << n); i++) {
		f[get(i)][i] = a[i];
		g[get(i)][i] = b[i];
	}
	for (int i = 0; i <= n; i++)
		OR(1 << n, f[i], 1), OR(1 << n, g[i], 1);
	for (int i = 0; i <= n; i++)
		for (int j = 0; j <= i; j++)
			for (int k = 0; k < (1 << n); k++)
				add(h[i][k], (LL)f[j][k] * g[i - j][k] % P);
	for (int i = 0; i <= n; i++) OR(1 << n, h[i], -1);
	for (int i = 0; i < (1 << n); i++) c[i] = h[get(i)][i];
}

数据结构

struct Fhq{
	int rt;
	void pushup(int p) {
		
	}
	// value(A) < value(B)
	int merge(int A, int B) {
		if (!A || !B) return A + B;
		else if(t[A].rnd > t[B].rnd) {
			t[A].r = merge(t[A].r, B);
			pushup(A);
			return A;
		} else {
			t[B].l = merge(A, t[B].l);
			pushup(B);
			return B;
		}
	}

	// 按值分裂
	void split(int p, int k, int &x, int &y) {
		if (!p) x = y = 0;
		else {
			if (t[p].val <= k) 
			x = p, split(t[p].r, k, t[p].r, y);
			else y = p, split(t[p].l, k, x, t[p].l);
			pushup(p);
		}
	}
	int getNode(int val) {
		t[++idx] = (T) { 0, 0, val, rand(), 1 };
		return idx;
	}

	void insert(int val) {
		int x, y;
		split(rt, val, x, y);
		rt = merge(merge(x, getNode(val)), y);
	}

	int get(int l, int r) {
		int x, y, z;
		split(rt, l - 1, x, y);
		split(y, r, y, z);
		int res = t[y].N;
		rt = merge(x, merge(y, z));
		return res;
	}

	void del(int val) {
		int x, y, z;
		split(rt, val - 1, x, y);
		split(y, val, y, z);
		y = merge(t[y].l, t[y].r);
		rt = merge(x, merge(y, z));
	}
}

struct LCT{
	#define get(x) (ch[fa[x]][1] == x)
	#define isRoot(x) (ch[fa[x]][0] != x && ch[fa[x]][1] != x)
	#define ls ch[p][0]
	#define rs ch[p][1]

	int ch[N][2], fa[N], mx[N], w[N], rev[N];

	void inline pushup(int p) {
		
	}

	void inline pushdown(int p) {
		if (rev[p]) { swap(ls, rs), rev[ls] ^= 1, rev[rs] ^= 1, rev[p] = 0; }
	}

	void inline rotate(int x) {
		int y = fa[x], z = fa[y], k = get(x);
		if (!isRoot(y)) ch[z][get(y)] = x;
		ch[y][k] = ch[x][!k], fa[ch[y][k]] = y;
		ch[x][!k] = y, fa[y] = x, fa[x] = z;
		pushup(y); pushup(x);
	}

	void inline update(int p) {
		if (!isRoot(p)) update(fa[p]);
		pushdown(p);
	}

	void inline splay(int p) {
		update(p);
		for (int f = fa[p]; !isRoot(p); rotate(p), f = fa[p]) 
			if (!isRoot(f)) rotate(get(p) == get(f) ? f : p);
	}

	void inline access(int x) {
		for (int p = 0; x; p = x, x = fa[x]) {
			splay(x), ch[x][1] = p, pushup(x);
		}
	}

	int inline find(int p) {
		access(p), splay(p);
		while (ls) pushdown(p), p = ls;
		splay(p);
		return p;
	}

	void inline makeRoot(int x) {
		access(x), splay(x), rev[x] ^= 1;
	}

	void inline split(int x, int y) {
		makeRoot(x), access(y), splay(y);
	}

	void inline link(int x, int y) {
		makeRoot(x), fa[x] = y;
	}

	void inline cut(int x, int y) {
		split(x, y);
		ch[y][0] = 0, fa[x] = 0;
		pushup(y);
	}

}

// 左偏树
struct LeftistTree{
	struct T{
	    int l, r, v, d, f;
	    // l, r 表示左右儿子, v 表示值
	    // d 表示从当前节点到最近叶子节点的距离, f 表示当前节点的父亲
	} t[SZ];

	int find(int x) {
	    return t[x].f == x ? x : t[x].f = find(t[x].f);
	}

	int merge(int x, int y) { // 递归合并函数
	    if (!x || !y) return x + y;
	    if (t[x].v > t[y].v || (t[x].v == t[y].v && x > y)) swap(x, y);
	    rs = merge(rs, y);
	    if (t[ls].d < t[rs].d) swap(ls, rs);
	    t[x].d = t[rs].d + 1;
	    return x;
	}

	int work(int x, int y) { // 合并 x, y 两个堆。
	    if (x == y) return 0;
		if (!x || !y) return t[x + y].f = x + y;
	    if (t[x].v > t[y].v || (t[x].v == t[y].v && x > y)) swap(x, y);
	    t[x].f = t[y].f = x;
	    merge(x, y); return x;
	}

	void del(int x) {
	    t[x].f = work(ls, rs), t[x].v = -1;
	}
}
// 李超树

struct LC{
	struct Tree{
		int l, r;
		Line v;
	} t[N << 2];
	LL inline calc(Line e, LL x) {
		return e.k * x + e.b;
	}
	int idx, rt;
	void inline clr() {
		idx = 0; rt = 0;
	}
	// 这里写法非常简洁的原因是,让计算机人工帮你判断了单调 / 需要 upd 的位置,事实上只会走一边。
	void inline ins(int &p, int l, int r, Line e) {
		if (!p) {
			t[p = ++idx] = (Tree) { 0, 0, e };
			return;
		}
		int mid = (l + r) >> 1;
		if (calc(t[p].v, mid) > calc(e, mid)) swap(e, t[p].v);
		if (calc(e, l) < calc(t[p].v, l)) ins(t[p].l, l, mid, e);
		if (calc(e, r) < calc(t[p].v, r)) ins(t[p].r, mid + 1, r, e); 
	}
	LL ask(int p, int l, int r, int x) {
		if (!p) return INF;
		if (l == r) return calc(t[p].v, x);
		int mid = (l + r) >> 1; LL ret = calc(t[p].v, x);
		if (x <= mid) chkMin(ret, ask(t[p].l, l, mid, x));
		else chkMin(ret, ask(t[p].r, mid + 1, r, x));
		return ret;
	}
	
} ;

计算几何

const double eps = 1e-4;
typedef pair<double, double> PDD;
struct Line{
    PDD s, t;
};

int inline cmp(double x, double y) {
    if (fabs(x - y) < eps) return 0;
    return x < y ? -1 : 1;
}

double inline cross(PDD a, PDD b) { return a.fi * b.se - a.se * b.fi; }
PDD operator - (const PDD &a, const PDD &b) { return make_pair(a.fi - b.fi, a.se - b.se); }
PDD operator + (const PDD &a, const PDD &b) { return make_pair(a.fi+ b.fi, a.se+ b.se); }
PDD operator / (const PDD &a, double b) { return make_pair(a.fi / b, a.se / b); }
PDD operator * (const PDD &a, double b) { return make_pair(a.fi * b, a.se * b); }
double inline area(PDD a, PDD b, PDD c) { return cross(b - a, c - a); }
double inline dot(PDD a, PDD b) { return a.fi * b.fi + a.se * b.se; }
double inline len(PDD a) { return sqrt(dot(a, a)); }
double inline project(PDD a, PDD b, PDD c) { return dot(b - a, c - a) / len(b - a); }
double inline dist(PDD a, PDD b) { return sqrt((a.fi - b.fi) * (a.fi - b.fi) + (a.se - b.se) * (a.se - b.se)); }
// 顺时针转 x
PDD inline rotate(PDD a, double x) { return make_pair ( cos(x) * a.fi + sin(x) * a.se, -sin(x) * a.fi + cos(x) * a.se ); }
PDD inline norm(PDD a) { return a / len(a); }
double angle(PDD a, PDD b) {
    return acos(dot(a, b) / len(a) / len(b));
}
int sign(double fi) {
    if (fabs(fi) < eps) return 0;
    if (fi < 0) return -1;
    return 1;
}


// 点到线段距离 

LD getD(PDD a, PDD u, PDD v) {
	LD w = min(dis(a, u), dis(a, v));
	LD c = dot(a - u, v - u);
	LD t = dis(u, v);
	c /= t;
	if (cmp(c, 0) >= 0 && cmp(c, t) <= 0) {
		LD z = norm(u - a);
		LD val = sqrt(z - c * c);
		w = val; 
	}
	return w;
} 


bool segInter(PDD a1, PDD a2, PDD b1, PDD b2) {
    double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1);
    double c3 = cross(b2 - b1, a2 - b1), c4 = cross(b2 - b1, a1 - b1);
    return sign(c1) * sign(c2) <= 0 && sign(c3) * sign(c4) <= 0;
}

bool cmp2 (const Line &a, const Line &b) {
    double A = getAngle(a), B = getAngle(b);
    if (A != B) return A < B;
    else return area(a.s, a.t, b.t) < 0;
}

PDD getInter(PDD p, PDD v, PDD q, PDD w) {
    PDD u = p - q;
    double t = cross(w, u) / cross(v, w);
    return make_pair(p.fi + t * v.fi, p.se + t * v.se);
}

PDD getInter(Line a, Line b) { return getInter(a.s, a.t - a.s, b.s, b.t - b.s); }

bool inline Right(Line a, Line b, Line c) {
    PDD u = getInter(b, c);
    return area(a.s, a.t, u) <= 0;
}

// 凸包

void inline andrew() {
    sort(p + 1, p + 1 + n);
    for (int i = 1; i <= n; i++) {
        while (top > 1 && area(p[s[top - 1]], p[s[top]], p[i]) < 0) {
            if (area(p[s[top - 1]], p[s[top]], p[i]) <= 0) st[s[top--]] = false;
            else top--;
        }
        st[i] = true, s[++top] = i;
    }
    st[1] = false;
    for (int i = n; i; i--) {
        if (!st[i]) {
            while (top > 1 && area(p[s[top - 1]], p[s[top]], p[i]) <= 0) 
                st[s[top--]] = false;
            st[i] = true, s[++top] = i;
        }
    }
    for (int i = 0; i < top; i++) s[i] = s[i + 1];
    top--;
}

struct Line{
    PDD s, t;
    int id;
} e[N];

// 半平面交
double HPI() {
    sort(e + 1, e + 1 + n, cmp2);
    int hh = 0, tt = -1;
    for (int i = 1; i <= n; i++) {
        if (i && getAngle(e[i]) == getAngle(e[i - 1])) continue;
        while (hh < tt && Right(e[i], e[q[tt - 1]], e[q[tt]])) tt--;
        while (hh < tt && Right(e[i], e[q[hh]], e[q[hh + 1]])) hh++;
        q[++tt] = i;
    }
    while (hh < tt && Right(e[q[hh]], e[q[tt - 1]], e[q[tt]])) tt--;
    while (hh < tt && Right(e[q[tt]], e[q[hh]], e[q[hh + 1]])) hh++;
    q[++tt] = q[hh];
    tot = 0;
    for (int i = hh; i < tt; i++)
        p[++tot] = getInter(e[q[i]], e[q[i + 1]]);
    double res = 0;
    for (int i = 1; i < tot; i++)
        res += area(p[1], p[i], p[i + 1]);
    return res / 2;
}

Point inline getCircle(Point a, Point b, Point c) {
    return Inter((a + b) / 2, rotate(b - a, PI / 2), (a + c) / 2, rotate(c - a, PI / 2));
}

// 最小圆覆盖

void inline minCircle(PDD a[]) {
    random_shuffle(a + 1, a + 1 + n);
    double r = 0; Point u = a[1]; 
    for (int i = 2; i <= n; i++) {
        if (cmp(r, len(u - a[i])) == -1) {
            r = 0, u = a[i];
            for (int j = 1; j < i; j++) {
                if (cmp(r, len(u - a[j])) == -1) {
                    r = len(a[i] - a[j]) / 2, u = (a[i] + a[j]) / 2;
                    for (int k = 1; k < j; k++) {
                        if (cmp(r, len(u - a[k])) == -1) {
                            u = getCircle(a[i], a[j], a[k]), r = len(a[i] - u);
                        } 
                    }
                }
            }
        }
    }
}


// 自适应辛普森积分
double inline f(double fi) {
    return ?;
}
double inline s(double l, double r) {
    double mid = (l + r) / 2;
    return (r - l) * (f(l) + 4 * f(mid) + f(r)) / 6;
}

double inline asr(double l, double r) {
    double mid = (l + r) / 2, v = s(l, r);
    double a = s(l, mid), b = s(mid, r);
    if (fabs(a + b - v) < eps) return v;
    else return asr(l, mid) + asr(mid, r);
}

// https://codeforces.com/contest/1284/problem/E 的怨念 不丢精度的极角排序

LL inline cross(PII x, PII y) {
    return 1ll * x.fi * y.se - 1ll * x.se * y.fi;
}

int inline quad(PII x) {
    if (x.fi >= 0 && x.se >= 0) return 1;
    if (x.fi <= 0 && x.se >= 0) return 2;
    if (x.fi <= 0 && x.se <= 0) return 3;
    if (x.fi >= 0 && x.se <= 0) return 4;
    return 0;
}

// PII andrew + mincowf


LL operator * (PII a, PII b) {
    return (LL)a.fi * b.se - (LL)a.se * b.fi;
}

PII operator + (PII a, PII b) {
    return mp(a.fi + b.fi, a.se + b.se);
}

PII operator - (PII a, PII b) {
    return mp(a.fi - b.fi, a.se - b.se);
}

LL dot (PII a, PII b) {
    return (LL)a.fi * a.se + (LL)b.fi * b.se;
}

vector<PII> inline andrew(vector<PII> a) {
    int n = a.size();
    top = 0;
    sort(a.begin(), a.end());
    for (int i = 0; i < n; i++) {
        while (top > 1 && (a[i] - a[s[top - 1]]) * (a[s[top]] - a[s[top - 1]]) > 0) {
            vis[s[top--]] = 0;
        }
        vis[i] = 1, s[++top] = i;
    }
    vis[0] = 0;
    for (int i = n - 1; i >= 0; i--) {
        if (!vis[i]) {
            while (top > 1 && (a[i] - a[s[top - 1]]) * (a[s[top]] - a[s[top - 1]]) > 0)
                vis[s[top--]] = 0;
            vis[i] = 1, s[++top] = i;
        }
    }
    --top;
    vector<PII> ret;
    for (int i = 1; i <= top; i++) ret.pb(a[s[i]]);
    for (int i = 0; i < n; i++) vis[i] = 0;
    return ret;
}

// 有

vector<PII> calc(vector<PII> a, vector<PII> b) {
    vector<PII> c;
    c.pb(a[0] + b[0]);
    vector<PII> dx, dy;
    for (int i = 1; i < a.size(); i++) dx.pb(a[i] - a[i - 1]);
    dx.pb(a[0] - a.back());
    for (int i = 1; i < b.size(); i++) dy.pb(b[i] - b[i - 1]);
    dy.pb(b[0] - b.back());
    int i = 0, j = 0;
    while (i < dx.size() && j < dy.size()) {
        if (dx[i] * dy[j] > 0)
            c.pb(c.back() + dx[i++]);
        else if (dx[i] * dy[j] == 0 && c.size() > 1) {
            // 共线放一起不然是错的!!!!
            if (dot(c.back() - c[c.size() - 2], dx[i]) > 0)
                c.pb(c.back() + dx[i++]);
            else c.pb(c.back() + dy[j++]);
        } else {
            c.pb(c.back() + dy[j++]);
        }
    }
    while (i < dx.size()) c.pb(c.back() + dx[i++]);
    while (j < dy.size()) c.pb(c.back() + dy[j++]);
    assert(c.back() == c[0]);
    c.pop_back();
    return c;
}
Frank Xiang
Frank Xiang

Experience and record the world.