constexpr int mod = 998244353;
const int N = 5e5 + 10;
// 组合数
int qp(int a, int b)
{if (!a)return 1ll;int res = 1;while (b){if (b & 1)res = res * a % mod; // 不要取模时记得取消a = a * a % mod;b >>= 1;}return res;
}int fac[N], inv[N]; // fac inv 阶乘与阶乘的逆元
void init()
{fac[0] = inv[0] = 1;for (int i = 1; i < N; ++i)fac[i] = fac[i - 1] * i % mod;inv[N - 1] = qp(fac[N - 1], mod - 2);for (int i = N - 2; i >= 1; --i)inv[i] = inv[i + 1] * (i + 1) % mod;
}
int C(int n, int m)
{return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
7. lucas定理求组合数
int qp(int a, int k, int p)
{int res = 1 % p;while (k){if (k & 1)res = res * a % p;a = a * a % p;k >>= 1;}return res;
}int C(int a, int b, int p) // 通过定理求组合数C(a, b)
{if (a < b)return 0;int x = 1, y = 1; // x是分子,y是分母for (int i = a, j = 1; j <= b; i--, j++){x = x * i % p;y = y * j % p;}return x * qp(y, p - 2, p) % p;
}int lucas(int a, int b, int p)
{if (a < p && b < p)return C(a, b, p);return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
// 线性基能使用异或运算来表示原数集使用异或运算能表示的所有数。
// 可以极大地缩小异或操作所需的查询次数。
// 1. 判断一个数能否表示成某数集子集的异或和
// 2. 求一个数表示成某数集子集异或和的方案数
// 3. 求某数集子集的最大/最小/第k大/第k小异或和
// 4. 求一个数在某数集子集异或和中的排名
// 5. 所有异或值
// 6. 任意一条 1 到 n 的路径的异或和,都可以由任意一条 1 到 n 路径的异或和与图中的一些环的异或和来组合得到。
struct T
{int bit[64];bool zero = 0;int cnt, top;int d[64];T(){zero = cnt = top = 0;memset(bit, 0, sizeof bit);memset(d, 0, sizeof d);}bool insert(int x){for (int i = 63; i >= 0; i--){if ((x >> i) & 1){if (!bit[i]){bit[i] = x;return 1;}elsex ^= bit[i];}}zero = 1;return false;}int ask(int x) // 询问是否能被异或出{for (int i = 63; i >= 0; i--)if (x >> i & 1)x ^= bit[i];return x == 0;}int askmx() // 异或最大值{int ans = 0;for (int i = 63; i >= 0; i--)if ((ans ^ bit[i]) > ans)ans ^= bit[i];return ans;}// rebuildvoid rebuild(){cnt = 0;top = 0;for (int i = 63; i >= 0; i--)for (int j = i - 1; j >= 0; j--)if (bit[i] & (1LL << j))bit[i] ^= bit[j];for (int i = 0; i <= 63; i++)if (bit[i])d[cnt++] = bit[i];}int kth(int k) // 异或第k小 需要rebuild{k -= zero;if (!k)return 0;if (k >= (1LL << cnt))return -1;int ans = 0;for (int i = 63; i >= 0; i--)if (k & (1LL << i))ans ^= d[i];return ans;}int rak(int x) // 查询排名{int ans = 0;for (int i = cnt - 1; i >= 0; i--)if (x >= d[i])ans += (1 << i), x ^= d[i];return ans + zero;}
};
10. 主席树
// 主席树 维护离散化后的坐标
// 找区间第k小值
struct Node
{int lc, rc;int cnt;
};
struct Tree
{int n, idx = 0;vector<Node> tr;vector<int> root;Tree(int n1){n = n1 + 2;tr.assign(n * 23, Node{}); // n*4+mlognroot.assign(n, 0);}int build(int l, int r){int p = ++idx;if (l == r)return p;int mid = l + r >> 1;tr[p].lc = build(l, mid);tr[p].rc = build(mid + 1, r);tr[p].cnt = tr[tr[p].lc].cnt + tr[tr[p].rc].cnt;return p;}int insert(int last, int l, int r, int x){int p = ++idx;tr[p] = tr[last];if (l == r){tr[p].cnt++;return p;}int mid = l + r >> 1;if (x <= mid)tr[p].lc = insert(tr[last].lc, l, mid, x);elsetr[p].rc = insert(tr[last].rc, mid + 1, r, x);tr[p].cnt = tr[tr[p].lc].cnt + tr[tr[p].rc].cnt;// pushup(p,tr[p].lc,tr[p].rc);return p;}int ask(int i, int j, int l, int r, int k){if (l == r)return l;int mid = l + r >> 1;int cnt = tr[tr[j].lc].cnt - tr[tr[i].lc].cnt;if (k <= cnt)return ask(tr[i].lc, tr[j].lc, l, mid, k);elsereturn ask(tr[i].rc, tr[j].rc, mid + 1, r, k - cnt);}
}; // Tree tr(n);void _()
{int n, m;cin >> n >> m;vector<int> a(n + 1);// 离散化 nums[find(x)] == x find(x):为x在nums中的指针vector<int> nums;for (int i = 1; i <= n; i++){cin >> a[i];nums.push_back(a[i]);}auto find = [&](int x){return lower_bound(nums.begin(), nums.end(), x) - nums.begin();};sort(nums.begin(), nums.end());nums.erase(unique(nums.begin(), nums.end()), nums.end());Tree tr(n); // 版本数tr.root[0] = tr.build(0, nums.size() - 1);for (int i = 1; i <= n; i++)tr.root[i] = tr.insert(tr.root[i - 1], 0, nums.size() - 1, find(a[i]));while (m--){int l, r, k;cin >> l >> r;k = (r - l + 1) / 2 + 1; // 查询第k小值cout << nums[tr.ask(tr.root[l - 1], tr.root[r], 0, nums.size() - 1, k)] << endl;}
}
11. 约瑟夫环
// 约瑟夫环 0~n-1
// O(n)
int josephus(int n, int k)
{int res = 0;for (int i = 1; i <= n; ++i)res = (res + k) % i;return res;
}
// O(klogn)
int josephus(int n, int k)
{if (n == 1)return 0;if (k == 1)return n - 1;if (k > n)return (josephus(n - 1, k) + k) % n; // 线性算法int res = josephus(n - n / k, k);res -= n % k;if (res < 0)res += n; // mod nelseres += res / (k - 1); // 还原位置return res;
}
12. tarjan 求静态LCA
void _() // tarjan 求静态LCA
{int n, q, root;cin >> n >> q >> root;vector<vector<int>> e(n + 1);vector<vector<pair<int, int>>> query(n + 1);vector<int> p(n + 1), vis(n + 1), ans(n + 1);for (int i = 1; i <= n; i++)p[i] = i;auto find = [&](auto &&self, int u) -> int{return p[u] = p[u] == u ? u : self(self, p[u]);};auto tarjan = [&](auto &&self, int u) -> void{vis[u] = 1;for (auto v : e[u]){if (!vis[v]){self(self, v);p[v] = u;}}for (auto [v, idx] : query[u]){if (vis[v])ans[idx] = find(find, v);}};for (int i = 1; i < n; i++){int u, v;cin >> u >> v;e[u].push_back(v);e[v].push_back(u);}for (int i = 1; i <= q; i++){int u, v;cin >> u >> v;query[u].push_back({v, i});query[v].push_back({u, i});}tarjan(tarjan, root);for (int i = 1; i <= q; i++)cout << ans[i] << '\n';
}
13. tarjan 求无向图割点
void _() // tarjan 求无向图割点
{int n, m;cin >> n >> m;vector<vector<int>> e(n + 1);vector<int> dfn(n + 1), low(n + 1), cut(n + 1);int tot = 0, root = 1, cuts = 0;for (int i = 0; i < m; i++){int u, v;cin >> u >> v;e[u].push_back(v);e[v].push_back(u);}auto tarjan = [&](auto &&self, int u) -> void{dfn[u] = low[u] = ++tot;int chd = 0;for (auto v : e[u]){if (!dfn[v]){self(self, v);low[u] = min(low[u], low[v]);if (low[v] >= dfn[u]){chd++;if (u - root || chd > 1)cut[u] = 1;}}elselow[u] = min(low[u], dfn[v]);}};for (int i = 1; i <= n; i++) // 图可不连通if (!dfn[i]){root = i;tarjan(tarjan, i);}for (int i = 1; i <= n; i++)cuts += cut[i];cout << cuts << '\n';for (int i = 1; i <= n; i++)if (cut[i])cout << i << ' ';
}
14. tarjan 求无向图割点后的连通块
void _() // tarjan 求无向图割点后的连通块
{int n, m;cin >> n >> m;vector<vector<int>> e(n + 1);vector<int> dfn(n + 1), low(n + 1), cut(n + 1);int tot = 0, root = 1, cuts = 0;vector<int> res(n + 1), sz(n + 1);for (int i = 0; i < m; i++){int u, v;cin >> u >> v;e[u].push_back(v);e[v].push_back(u);}auto tarjan = [&](auto &&self, int u) -> void{dfn[u] = low[u] = ++tot;int chd = 0;sz[u] = 1;int ss = 1;for (auto v : e[u]){if (!dfn[v]){self(self, v);sz[u] += sz[v]; // dfs搜索树low[u] = min(low[u], low[v]);if (low[v] >= dfn[u]) // 不含返祖边的子树才能作为真子树 其余作为上子树{chd++;ss += sz[v]; // ss 才是割点为子树的res[u] += sz[v] * (n - sz[v]);if (u - root || chd > 1)cut[u] = 1;}}elselow[u] = min(low[u], dfn[v]);}res[u] += ss * (n - ss) + n - 1;if (!cut[u])res[u] = n - 1 << 1;};tarjan(tarjan, root);// for (int i = 1; i <= n; i++)// bug({i, sz[i]});for (int i = 1; i <= n; i++)cout << res[i] << '\n';
}
15. tarjan有向图强连通分量缩点
void _() // tarjan有向图强连通分量缩点
{int n, m;cin >> n >> m;vector<vector<int>> e(n + 1);vector<pair<int, int>> edges;vector<int> w(n + 1);for (int i = 1; i <= n; i++)cin >> w[i];for (int i = 1; i <= m; i++){int u, v;cin >> u >> v;e[u].push_back(v);edges.push_back({u, v});}int tot = 0;vector<int> dfn(n + 1), low(n + 1), stk(n + 1), instk(n + 1), belong(n + 1);// 时间戳 追溯值 栈维护返祖边和横插边能到达的点(祖先节点和左子树节点)belong属于哪个点代表的强连通分量vector<int> scc(n + 1), sz(n + 1); // 记录点在哪个强连通分量中 强连通分量大小int cnt = 0, top = 0; // 强连通分量编号auto tarjan = [&](auto &&self, int u) -> void{// 进入u,盖戳、入栈dfn[u] = low[u] = ++tot;stk[++top] = u, instk[u] = 1;for (auto v : e[u]){if (!dfn[v]){self(self, v);low[u] = min(low[u], low[v]);}else if (instk[v]) // 已访问且在栈中low[u] = min(low[u], low[v]);}// 离u,记录SCCif (dfn[u] == low[u]){int v;++cnt;do{v = stk[top--];instk[v] = 0;scc[v] = cnt;++sz[cnt];belong[v] = u;if (u - v)w[u] += w[v];} while (u - v);}};for (int i = 1; i <= n; i++){if (!dfn[i])tarjan(tarjan, i);}// 缩点vector<int> in(n + 1);vector<vector<int>> ne(n + 1);for (auto [u, v] : edges){u = belong[u], v = belong[v];if (u - v){ne[u].push_back(v);in[v]++;}}auto topo = [&](){vector<int> f(n + 1);queue<int> q;for (int i = 1; i <= n; i++){if (belong[i] == i && !in[i]) // dfn[i] == low[i]{q.push(i);f[i] = w[i];}}while (q.size()){auto u = q.front();q.pop();for (auto v : ne[u]){f[v] = max(f[v], w[v] + f[u]);in[v]--;if (!in[v])q.push(v);}}return f;};auto dp = topo();int res = 0;for (int i = 1; i <= n; i++){res = max(res, dp[i]);}cout << res << '\n';
}
16. exgcd
// exgcd 解同余方程ax+by==c ax==c(mod b)
int ex_gcd(int a, int b, int &x, int &y)
{if (b == 0){x = 1;y = 0;return a;}int d = ex_gcd(b, a % b, x, y);int temp = x;x = y;y = temp - a / b * y;return d;
}bool liEu(int a, int b, int c, int &x, int &y)
{int d = ex_gcd(a, b, x, y);if (c % d != 0)return false;int k = c / d;x *= k;y *= k;return true;
} // 最小正整数解
17. bsgs
int exgcd(int a, int b, int& x, int& y) {if (!b) {x = 1, y = 0;return a;}int d = exgcd(b, a % b, y, x);y -= a / b * x;return d;
}ll bsgs(int a, int b, int p) {if (1 % p == b % p) return 0; // 特判t=0,因p可能为1,故写1%pint k = sqrt(p) + 1; // 分块数// 预处理出所有b * a^y (mod p)umap<int, int> hash;for (int i = 0, j = b % p; i < k; i++) {hash[j] = i; // 记录值的同时记录对应的y,较大的y会覆盖较小的yj = (ll)j * a % p;}// 预处理出a^kint ak = 1;for (int i = 0; i < k; i++) ak = (ll)ak * a % p;// 遍历x∈[1,k],在哈希表中查找是否存在满足的yfor (int i = 1, j = ak; i <= k; i++) { // j记录(a^k)^xif (hash.count(j)) return (ll)i * k - hash[j]; // 返回t值j = (ll)j * ak % p;}return -1; // 无解
}void solve() {int a, b, m, x0, x; cin >> a >> b >> m >> x0 >> x;if (x0 == x) {cout << "YES";return;}int x1 = ((ll)a * x0 + b) % m;if (a == 0) { // 特判if (x1 == x || b == x) cout << "YES";else cout << "NO";}else if (a == 1) { // 特判if (!b) cout << (x == x1 ? "YES" : "NO");else {// 求不定方程(n-1)b + mp = t - X0的最小正整数解int x, y;exgcd(b, m, x, y);x = ((ll)x * (x - x1) % m + m) % m; // 保证x是正数cout << (~x ? "YES" : "NO");}}else {int C = (ll)b * qpow(a - 1, m - 2, m) % m; // b/(a-1)int A = (x1 + C) % m; // 分母if (!A) { // 特判A=0int ans = (-C + m) % m;cout << (ans == x ? "YES" : "NO");}else {int B = (x + C) % m; // 分子int ans = bsgs(a, (ll)B * qpow(A, m - 2, m) % m, m);cout << (~ans ? "YES" : "NO");}}
}
18. exbsgs
// bsgs exbsgs
// 解同余方程 a^x==b(mod p) x的最小解
map<int, int> mp;
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
int BSGS(int a, int n, int p, int ad)
{mp.clear();int m = std::ceil(std::sqrt(p));int s = 1;for (int i = 0; i < m; i++, s = 1ll * s * a % p)mp[1ll * s * n % p] = i;for (int i = 0, tmp = s, s = ad; i <= m; i++, s = 1ll * s * tmp % p)if (mp.find(s) != mp.end())if (1ll * i * m - mp[s] >= 0)return 1ll * i * m - mp[s];return -1;
}int exBSGS(int a, int b, int p)
{int n = b;a %= p;n %= p;if (n == 1 || p == 1)return 0;int cnt = 0;int d, ad = 1;while ((d = gcd(a, p)) ^ 1){if (n % d)return -1;cnt++;n /= d;p /= d;ad = (1ll * ad * a / d) % p;if (ad == n)return cnt;}// std::printf("a: %d n: %d p: %d ad:%d\n",a,n,p,ad);int ans = BSGS(a, n, p, ad);if (ans == -1)return -1;return ans + cnt;
}