当前位置: 首页 > news >正文

板子 --11.6

板子 --11.6

目录

1. 带权并查集
2. 虚树
3. Trie(新版
4. 01-Trie
5. tarjan有向图强连通分量缩点
6. EBCC-边双连通分量
7. BCC-割点与点双连通分量
8. 双hash
9. 倍增lca
10. 倍增
11. 后缀数组
12. 矩阵快速幂
13. 计算几何(ll快ld6倍
14. 线段树-结合律-函数复合
15. 整除分块
16. 杂项
17. 单hash
18. Z函数

内容

1. 带权并查集
// 带权并查集
struct DSU
{vector<int> p, vs, es; // 集合数 点数 边数 (图:对一个连通块而言)vector<int> xor_val;   // 维护每个点与父节点之间的路径异或和DSU(int n1)            // p[x]不一定为根节点  find(x)一定是根节点{int n = n1 + 10;p.assign(n, 0);vs.assign(n, 0);es.assign(n, 0);xor_val.assign(n, 0);for (int i = 1; i <= n1; i++)p[i] = i, vs[i] = 1, es[i] = 0;}int find(int x) // 找到根节点{if (p[x] == x)return x;int px = find(p[x]);xor_val[x] ^= xor_val[p[x]];return p[x] = px;}bool same(int a, int b) { return find(a) == find(b); }int merge(int a, int b, int val = 0) // 合并集合 返回:-1 该边不会形成环  >=0 简单环的边权异或和{int pa = find(a);int pb = find(b);// if (pa > pb)//     swap(pa, pb);// mn[pa] = min({mn[pa], val, mn[pb]});if (pa == pb) // pa pb 均为根节点 p[pa]==pa{es[pa]++; // 1个集合 边+1return xor_val[a] ^ xor_val[b] ^ val;}p[pb] = pa; // 改变b的根节点xor_val[pb] = xor_val[a] ^ xor_val[b] ^ val;vs[pa] += vs[pb];     // 将b合并进aes[pa] += es[pb] + 1; // 2个集合return -1;}int size(int a) { return vs[find(a)]; } //  集合内的元素的个数
}; //  DSU dsu(n);
2. 虚树
// Virtual Tree  具有相同属性的一类点的 LCA
// 虚树常常被使用在树形dp中,当一次询问仅仅涉及到整颗树中少量结点时,为每次询问都对整棵树进行dp在时间上是不可接受的。
// 我们建立一颗仅仅包含部分关键结点的虚树,将非关键点构成的链简化成边或是剪去,在虚树上进行dp。
struct Edge
{int v, w;
};
using Tree = vector<vector<Edge>>;struct VirtualTree
{int n, LOG;Tree e;vector<int> dep, dfn, tin;vector<vector<int>> fa, minW, maxW, sumW;int timer = 0;VirtualTree() {}VirtualTree(int _n, const Tree &_e){n = _n;e = _e;LOG = __lg(n) + 1;dep.assign(n + 1, 0);dfn.assign(n + 1, 0);tin.assign(n + 1, 0);fa.assign(n + 1, vector<int>(LOG));minW.assign(n + 1, vector<int>(LOG, INT_MAX));maxW.assign(n + 1, vector<int>(LOG, INT_MIN));sumW.assign(n + 1, vector<int>(LOG, 0));dfs(1, 0, 0, 0);}void dfs(int u, int p, int w, int d){dep[u] = d;tin[u] = ++timer;fa[u][0] = p;minW[u][0] = (p ? w : INT_MAX);maxW[u][0] = (p ? w : INT_MIN);sumW[u][0] = (p ? w : 0);for (int i = 1; i < LOG; i++){int f = fa[u][i - 1];fa[u][i] = fa[f][i - 1];minW[u][i] = min(minW[u][i - 1], minW[f][i - 1]);maxW[u][i] = max(maxW[u][i - 1], maxW[f][i - 1]);sumW[u][i] = sumW[u][i - 1] + sumW[f][i - 1];}for (auto [v, w2] : e[u]){if (v == p)continue;dfs(v, u, w2, d + 1);}}int lca(int u, int v) const{if (dep[u] < dep[v])swap(u, v);int diff = dep[u] - dep[v];for (int i = LOG - 1; i >= 0; i--)if (diff >> i & 1)u = fa[u][i];if (u == v)return u;for (int i = LOG - 1; i >= 0; i--)if (fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];return fa[u][0];}int queryUp(int u, int anc, int type) const{int ans;if (type == 0)ans = INT_MAX;if (type == 1)ans = INT_MIN;if (type == 2)ans = 0;int diff = dep[u] - dep[anc];for (int i = LOG - 1; i >= 0; i--){if (diff >> i & 1){if (type == 0)ans = min(ans, minW[u][i]);if (type == 1)ans = max(ans, maxW[u][i]);if (type == 2)ans += sumW[u][i];u = fa[u][i];}}return ans;}int pathMin(int u, int v) const{int g = lca(u, v);return min(queryUp(u, g, 0), queryUp(v, g, 0));}int pathMax(int u, int v) const{int g = lca(u, v);return max(queryUp(u, g, 1), queryUp(v, g, 1));}int pathSum(int u, int v) const{int g = lca(u, v);return queryUp(u, g, 2) + queryUp(v, g, 2);}// 返回 {tr, {ids, root}}pair<vector<vector<pair<int, int>>>, pair<vector<int>, int>>getVirtualTree(vector<int> h, function<int(int, int)> calcFunc){sort(h.begin(), h.end(), [&](int a, int b){ return tin[a] < tin[b]; });h.erase(unique(h.begin(), h.end()), h.end());int sz = (int)h.size();for (int i = 0; i + 1 < sz; i++)h.push_back(lca(h[i], h[i + 1]));sort(h.begin(), h.end(), [&](int a, int b){ return tin[a] < tin[b]; });h.erase(unique(h.begin(), h.end()), h.end());vector<int> a = h;int m = (int)a.size();unordered_map<int, int> idx;idx.reserve(m * 2);for (int i = 0; i < m; ++i)idx[a[i]] = i;vector<vector<pair<int, int>>> tr(m);vector<int> st;st.push_back(a[0]);for (int i = 1; i < m; i++){int u = a[i];int lc = lca(u, st.back());while (st.size() >= 2 && dep[st[st.size() - 2]] >= dep[lc]){int v = st.back();st.pop_back();int p = st.back();int w = calcFunc(p, v);tr[idx[p]].push_back({idx[v], w});}if (st.back() != lc){int v = st.back();st.pop_back();int p = lc;int w = calcFunc(p, v);tr[idx[p]].push_back({idx[v], w});st.push_back(lc);}st.push_back(u);}while (st.size() >= 2){int v = st.back();st.pop_back();int p = st.back();int w = calcFunc(p, v);tr[idx[p]].push_back({idx[v], w});}int root = idx[st[0]];return {tr, {a, root}};}
};
// 构建虚树,边权为路径边权最小值
// auto res = vtr.getVirtualTree(h, [&](int u, int v) { return vtr.pathMin(u, v); });
// auto &tr = res.first; // ids[idx] = 原树节点编号
// auto [ids, root] = res.second;
3. Trie(新版
// 字典树 用来快速查询多个字符串某前缀的个数
// 也用来快速求数组中两个数的最大异或值/>=k的异或值
struct Trie
{int sumLen = 1;vector<array<int, 62>> nx;vector<int> cnt;int idx = 0;Trie(vector<string> &vs){for (auto s : vs)sumLen += s.size();nx.assign(sumLen, array<int, 62>{});cnt.assign(sumLen, 0);for (auto s : vs)insert(s);}int getNum(char x) // 大小写字母和数字{if (x >= 'A' && x <= 'Z')return x - 'A'; // 'A' 到 'Z' 映射到 0-25else if (x >= 'a' && x <= 'z')return x - 'a' + 26; // 'a' 到 'z' 映射到 26-51else if (x >= '0' && x <= '9')return x - '0' + 52; // '0' 到 '9' 映射到 52-61elsereturn -1; // 非法字符返回 -1}void insert(string s){int p = 0;for (auto c : s){int x = getNum(c);if (!nx[p][x])nx[p][x] = ++idx;p = nx[p][x];cnt[p]++;}}int find(string s){int p = 0;for (auto c : s){int x = getNum(c);if (!nx[p][x])return 0;p = nx[p][x];}return cnt[p];}int findMaxXor(string s){int p = 0, len = 0;int ans = 0;for (int i = 0; i < 31; i++){int x = getNum(s[i]);// bug(i, s[i], x);if (nx[p][x ^ 1])ans += 1 << 30 - i, p = nx[p][x ^ 1]; //, bug(i, x ^ 1);elsep = nx[p][x];}return ans;}
}; //  Trie tr(vs);
4. 01-Trie
// 01-Trie 用来快速求数组中两个数的最大异或值/>=k的异或值
constexpr int N = 200000 * 31;
int tot;
int trie[N][2];
int cnt[N];
int newNode()
{int x = ++tot;trie[x][0] = trie[x][1] = 0;cnt[x] = 0;return x;
}
void init()
{tot = 0;newNode();
}
void add(int x, int t) // 1加入 -1删除
{int o = 1;for (int i = 30; i >= 0; i--){int &p = trie[o][x >> i & 1];if (!p)p = newNode();o = p;cnt[o] += t;}
}
int query(int x) // 查询01-Trie里与x异或的最大值
{int o = 1;int ans = 0;for (int i = 30; i >= 0; i--){int d = x >> i & 1;if (cnt[trie[o][d ^ 1]]){d ^= 1;ans |= 1 << i;}o = trie[o][d];}return ans;
} // init();
5. 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';
}
6. EBCC-边双连通分量
struct EBCC {struct Edge {int to, id;};int n;std::vector<std::vector<Edge>> adj;std::vector<int> stk, dfn, low, bel;int cur, cnt, edge_id;EBCC(int n=0) { init(n); }void init(int n) {this->n = n;adj.assign(n, {});dfn.assign(n, -1);low.resize(n);bel.assign(n, -1);stk.clear();cur = cnt = edge_id = 0;}void addEdge(int u, int v) {if (u == v) { // 自环不进入 Tarjan 树,但可以在压缩阶段统计return;}adj[u].push_back({v, edge_id});adj[v].push_back({u, edge_id});edge_id++;}void dfs(int x, int in_edge) {dfn[x] = low[x] = cur++;stk.push_back(x);for (auto e : adj[x]) {int y = e.to;if (e.id == in_edge) continue; // 不走刚来的那条边if (dfn[y] == -1) {dfs(y, e.id);low[x] = std::min(low[x], low[y]);} else if (bel[y] == -1) {low[x] = std::min(low[x], dfn[y]);}}if (dfn[x] == low[x]) {int y;do {y = stk.back();bel[y] = cnt;stk.pop_back();} while (y != x);cnt++;}}std::vector<int> work() {for (int i=0; i<n; i++) {if (dfn[i] == -1) dfs(i, -1);}return bel;}struct Graph {int n;std::vector<std::pair<int,int>> edges;std::vector<int> siz, cnte;};Graph compress() {Graph g;g.n = cnt;g.siz.resize(cnt);g.cnte.resize(cnt);for (int i=0; i<n; i++) {g.siz[bel[i]]++;for (auto e : adj[i]) {int j = e.to;if (bel[i] < bel[j]) {g.edges.emplace_back(bel[i], bel[j]);} else if (i < j) {g.cnte[bel[i]]++;}}}return g;}
}; //  EBCC g(n);   g.addEdge(u, v); auto bel = g.work();   auto compressedGraph = ebcc.compress();
7. BCC-割点与点双连通分量
struct BCC {int n, dfs_clock, bcc_cnt;vector<vector<int>> adj;    // 原图邻接表vector<int> dfn, low;       // Tarjan 数组vector<pair<int,int>> stk;  // 边栈vector<vector<int>> bcc;    // 存放每个点双vector<int> isCut;          // 割点标记BCC(int n = 0) { init(n); }void init(int n) {this->n = n;adj.assign(n, {});dfn.assign(n, -1);low.assign(n, 0);isCut.assign(n, 0);stk.clear();bcc.clear();dfs_clock = 0;bcc_cnt = 0;}// 加边,支持重边和自环void addEdge(int u, int v) {adj[u].push_back(v);adj[v].push_back(u);}void dfs(int u, int parent = -1) {dfn[u] = low[u] = ++dfs_clock;int child = 0;for (int v : adj[u]) {if (dfn[v] == -1) {stk.emplace_back(u, v); // 记录树边dfs(v, u);low[u] = min(low[u], low[v]);// 找到一个点双if (low[v] >= dfn[u]) {if (parent != -1 || ++child > 1)isCut[u] = 1;vector<int> comp;while (true) {auto e = stk.back();stk.pop_back();comp.push_back(e.first);comp.push_back(e.second);if (e.first == u && e.second == v) break;}sort(comp.begin(), comp.end());comp.erase(unique(comp.begin(), comp.end()), comp.end());bcc.push_back(comp);bcc_cnt++;}}else if (v != parent && dfn[v] < dfn[u]) {// 注意:允许重边和自环,自环也会进栈stk.emplace_back(u, v);low[u] = min(low[u], dfn[v]);}}}void work() {for (int i = 0; i < n; i++) {if (dfn[i] == -1) {dfs(i);// 清理残余边if (!stk.empty()) {vector<int> comp;while (!stk.empty()) {auto e = stk.back(); stk.pop_back();comp.push_back(e.first);comp.push_back(e.second);}sort(comp.begin(), comp.end());comp.erase(unique(comp.begin(), comp.end()), comp.end());bcc.push_back(comp);bcc_cnt++;}}}}
};
8. 双hash
using i64 = long long;
using PLL = pair<i64, i64>;
struct DoubleHash
{const i64 Base1 = 29, MOD1 = 1e9 + 7;const i64 Base2 = 131, MOD2 = 1e9 + 9;vector<i64> ha1, ha2, pow1, pow2;vector<i64> rha1, rha2;int len;DoubleHash() {}DoubleHash(string &s){init(s);}void init(string &s){len = s.size();ha1.resize(len + 1), ha2.resize(len + 1);pow1.resize(len + 1), pow2.resize(len + 1);rha1.resize(len + 1), rha2.resize(len + 1);s = " " + s;pow1[0] = pow2[0] = 1;for (int i = 1; i <= len; i++){pow1[i] = pow1[i - 1] * Base1 % MOD1;pow2[i] = pow2[i - 1] * Base2 % MOD2;}for (int i = 1; i <= len; i++){ha1[i] = (ha1[i - 1] * Base1 + s[i]) % MOD1;ha2[i] = (ha2[i - 1] * Base2 + s[i]) % MOD2;rha1[i] = (rha1[i - 1] * Base1 + s[len - i + 1]) % MOD1;rha2[i] = (rha2[i - 1] * Base2 + s[len - i + 1]) % MOD2;}}PLL get(int l, int r){i64 res1 = ((ha1[r] - ha1[l - 1] * pow1[r - l + 1]) % MOD1 + MOD1) % MOD1;i64 res2 = ((ha2[r] - ha2[l - 1] * pow2[r - l + 1]) % MOD2 + MOD2) % MOD2;return {res1, res2};}// 反哈希PLL get_rhash(int l, int r){i64 res1 = ((rha1[len - l + 1] - rha1[len - r] * pow1[r - l + 1]) % MOD1 + MOD1) % MOD1;i64 res2 = ((rha2[len - l + 1] - rha2[len - r] * pow2[r - l + 1]) % MOD2 + MOD2) % MOD2;return {res1, res2};}// 判断s[l, r]是否为回文串bool is_palindrome(int l, int r){return get(l, r) == get_rhash(l, r);}PLL add(PLL aa, PLL bb){i64 res1 = (aa.first + bb.first) % MOD1;i64 res2 = (aa.second + bb.second) % MOD2;return {res1, res2};}// aa *= Base的k次方PLL mul(PLL aa, i64 kk){i64 res1 = aa.first * pow1[kk] % MOD1;i64 res2 = aa.second * pow2[kk] % MOD2;return {res1, res2};}// 拼接字符串 r1 < l2  s = s1 + s2PLL link(int l1, int r1, int l2, int r2){return add(mul(get(l1, r1), r2 - l2 + 1), get(l2, r2));}
};
9. 倍增lca
const int N = 2e5 + 10;
int n, m;
vector<int> e[N];
int dep[N];
int fa[N][20];
void dfs(int u, int father)
{dep[u] = dep[father] + 1; // 设置当前节点深度fa[u][0] = father;        // 直接父节点// 预处理二进制提升表for (int i = 1; i <= 19; i++)fa[u][i] = fa[fa[u][i - 1]][i - 1];// 递归处理子节点for (int v : e[u])if (v != father)dfs(v, u);
}
int lca(int u, int v)
{// 确保u是较深的节点if (dep[u] < dep[v])swap(u, v);// 将u提升到与v同一深度for (int i = 19; i >= 0; i--)if (dep[fa[u][i]] >= dep[v])u = fa[u][i];if (u == v)return v; // 如果已经是同一节点// 同时提升u和v直到它们的父节点相同for (int i = 19; i >= 0; i--)if (fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];return fa[u][0]; // 返回LCA
}
11. 倍增
const int N = 2e5 + 10, lg = 23;
int fa[N][lg], mx[N][lg], mn[N][lg];for (int j = 1; j < lg; j++) // 初始化[i][0]for (int i = 1; i <= n; i++){fa[i][j] = fa[fa[i][j - 1]][j - 1];mx[i][j] = max(mx[i][j - 1], mx[fa[i][j - 1]][j - 1]);mn[i][j] = min(mn[i][j - 1], mn[fa[i][j - 1]][j - 1]);}// 预处理次小值
mx2[x][i]=max(max(mx2[f[x][i-1]][i-1],mx2[x][i-1]),mx[x][i-1]==mx[f[x][i-1]][i-1]?0:min(mx[x][i-1],mx[f[x][i-1]][i-1]));
12. 后缀数组
const int N = 1000010; // 找到字典序最大的前缀/后缀
// 𝑠𝑎[𝑖]表示将所有后缀排序后第 𝑖 小的后缀的编号
// 𝑟𝑘[𝑖]表示后缀 𝑖 的排名
// "21">"2" 数字比较字典序可能颠倒 特殊处理
int n, m = 1e5 + 10; // n需要全局 m:数字时设置为值域
int s[N];
int sa[N], x[N], y[N], c[N], rk[N], height[N];void get_sa()
{for (int i = 1; i <= n; i++)c[x[i] = s[i]]++;for (int i = 1; i <= m; i++)c[i] += c[i - 1];for (int i = n; i; i--)sa[c[x[i]]--] = i;for (int k = 1; k <= n; k <<= 1){int num = 0;for (int i = n - k + 1; i <= n; i++)y[++num] = i;for (int i = 1; i <= n; i++)if (sa[i] > k)y[++num] = sa[i] - k;for (int i = 1; i <= m; i++)c[i] = 0;for (int i = 1; i <= n; i++)c[x[i]]++;for (int i = 1; i <= m; i++)c[i] += c[i - 1];for (int i = n; i; i--)sa[c[x[y[i]]]--] = y[i], y[i] = 0;swap(x, y);x[sa[1]] = 1, num = 1;for (int i = 2; i <= n; i++)x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;if (num == n)break;m = num;}for (int i = 1; i <= n; i++)rk[sa[i]] = i;
}
13. 矩阵快速幂
struct Mat
{int n, m;vector<vector<int>> mat;Mat(int _n, int _m) : n(_n), m(_m), mat(_n, vector<int>(_m, 0)) {} // 下标从0开始const vector<int> &operator[](int i) const { return mat[i]; }      // 只读访问vector<int> &operator[](int i) { return mat[i]; }                  // 可写访问Mat operator*(const Mat &b) const{ // 矩阵乘法assert(m == b.n);Mat res(n, b.m);for (int i = 0; i < n; i++)for (int j = 0; j < b.m; j++)for (int k = 0; k < m; k++)res[i][j] = (res[i][j] + mat[i][k] * b[k][j]) % mod;for (auto vec : res.mat){for (auto v : vec)assert(v >= 0);}return res;}Mat pow(int k) const{ // 矩阵快速幂  须接收返回矩阵assert(n == m);Mat res(n, n);for (int i = 0; i < n; i++)res[i][i] = 1;Mat a = *this;while (k){if (k & 1)res = res * a;a = a * a;k >>= 1;}return res;}
};
14. 计算几何(ll快ld6倍

using ld = long double;
const ld eps = 1e-8; // 9 12 15
const ld PI = acos(-1);
#define Pt Point<T>
#define Pd Point<ld>
#define Pi Point<int>
#define Lt Line<T>
#define Ld Line<ld>
#define Li Line<int>
#define tmp template <class T>int sign(ld x) // 判符号
{if (abs(x) < eps)return 0;elsereturn x < 0 ? -1 : 1;
}
bool equal(ld x, ld y) { return !sign(x - y); } // 近似
bool big(ld has, ld tar) { return sign(has - tar) > 0; }////////////////////////////////////////////////////////////////////////////  点类//  点与向量
template <class T>
struct Point
{T x, y;Point(T x = 0, T y = 0) : 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); }Point operator*(T k) const { return Point(x * k, y * k); }Point operator/(T k) const { return Point(1.0 * x / k, 1.0 * y / k); }friend bool operator<(Point a, Point b){if (!equal(a.x, b.x))return a.x < b.x;return a.y < b.y;}friend bool operator>(Point a, Point b) { return b < a; }friend bool operator==(Point a, Point b) { return equal(a.x, b.x) && equal(a.y, b.y); } // 近似friend bool operator!=(Point a, Point b) { return !(a == b); }friend auto &operator>>(istream &is, Point &p) { return is >> p.x >> p.y; }friend auto &operator<<(ostream &os, Point p) { return os << "(" << p.x << ", " << p.y << ")"; }
};tmp ld len(Pt a) { return sqrt(a.x * a.x + a.y * a.y); };           // 模长
tmp ld dis(Pt a, Pt b) { return len(a - b); }                       // 两点距离
tmp ld dis1(Pt a, Pt b) { return abs(a.x - b.x) + abs(a.y - b.y); } // 曼哈顿距离tmp T cross(Pt a, Pt b) { return a.x * b.y - a.y * b.x; } // 叉积 ab*sin 两点与原点三角形面积  判两点位置关系两侧
tmp T cross(Pt p0, Pt p1, Pt p2) { return cross(p1 - p0, p2 - p0); }
tmp T dot(Pt a, Pt b) { return a.x * b.x + a.y * b.y; } // 点积 ab*cos  结合判两点象限位置关系
tmp T dot(Pt p0, Pt p1, Pt p2) { return dot(p1 - p0, p2 - p0); }
tmp ld angle(Pt a, Pt b) { return atan2(cross(a, b), dot(a, b)); } // 向量夹角[-pi, pi]  非负fabsl
tmp ld angle(Pt p) { return atan2l(p.y, p.x); }                    // 极角tmp ld area3(Pt a, Pt b, Pt c) { return abs(1.0 * cross(a, b, c)) / 2; } // 三角形面积tmp bool angle0(Pt a, Pt b) { return sqrt(cross(a, b)) < eps && dot(a, b) > 0; } // 夹角为0
tmp bool angle180(Pt a, Pt b) { return cross(a, b) < eps && !angle0(a, b); }     // 夹角为180tmp Pd standardize(Pt h) // 转换为单位向量
{ld l = len(h);if (sign(l) == 0)return {0, 0}; // 防止除零return h / l;
}
tmp Pd rotate(Pt p, ld rad) { return {p.x * cos(rad) - p.y * sin(rad), p.x * sin(rad) + p.y * cos(rad)}; } // 向量旋转任意角度tmp Pt rotate90(Pt a) { return Pt{-a.y, a.x}; }                                       // 旋转90° 垂直向量
tmp ld toDeg(ld x) { return x * 180 / PI; }                                           // 弧度转角度
tmp ld toArc(ld x) { return PI / 180 * x; }                                           // 角度转弧度
tmp ld angle(T a, T b, T c) { return acos((a * a + b * b - c * c) / (2.0 * a * b)); } // 余弦定理 三边求角C弧度tmp void psort(vector<Pt> &ps, Pt o = Pt(0, 0)) // 选定极点 极角排序 ✅
{// 三四一二 [ (0, -1), (1, 0), (0, 1), (-1, 0), ]sort(ps.begin(), ps.end(), [&](Pt &A, Pt &B){ A=A-o,B=B-o;auto f=[](Pt A){return A.y>0||(A.y==0&&A.x<0);};if(f(A)!=f(B))return f(A)<f(B);else if(cross(A,B)!=0)return cross(A,B)>0;return dis(A,{0,0})<dis(B,{0,0}); });
}////////////////////////////////////////////////////////////////////////  直线与线段// 直线
template <class T>
struct Line
{Point<T> a, b;Line(Pt a_ = {}, Pt b_ = {}) : a(a_), b(b_) {}friend auto &operator<<(ostream &os, Line l) { return os << "<" << l.a << ", " << l.b << ">"; }
};
tmp bool pointOnLineLeft(Pt p, Lt l) { return cross(l.b - l.a, p - l.a) > 0; } // 点在直线左侧 ✅
tmp bool onLine(Pt a, Pt b, Pt c) { return sign(cross(a - b, c - b)) == 0; }   // 点是否在直线上(三点是否共线)✅
tmp bool onLine(Pt p, Lt l) { return onLine(p, l.a, l.b); }
// 线段:点是否在线段上 ✅
tmp bool pointOnSegment(Pt p, Lt l) { return abs(cross(l.a - p, l.b - p)) < eps && dot(l.a - p, l.b - p) <= 0; }
tmp bool Parallel(Pt p1, Pt p2) { return equal(0, cross(p1, p2)); }                              // 平行 ✅
tmp bool Orthogonal(Pt p1, Pt p2) { return equal(0, dot(p1, p2)); }                              // 垂直 ✅
tmp Ld midSegment(Lt l) { return {(l.a + l.b) / 2.0, (l.a + l.b) / 2.0 + rotate90(l.a - l.b)}; } // 线段的中垂线tmp ld disPointToLine(Pt p, Lt l, bool isSegment = false) // 点到直线/线段的最近距离 ✅
{ld len_ab = dis(l.a, l.b);if (sign(len_ab) == 0)return dis(p, l.a);if (!isSegment){ld ans = cross(p, l.a, l.b);return abs(ans) / dis(l.a, l.b); // 面积除以底边长}else{if (dot(p - l.a, l.b - l.a) <= 0)return dis(p, l.a);if (dot(p - l.b, l.a - l.b) <= 0)return dis(p, l.b);ld ans = cross(p, l.a, l.b);return abs(ans) / dis(l.a, l.b);}
}
15. 线段树-结合律-函数复合
struct Node
{mint k, b;
};
#define ls u << 1
#define rs u << 1 | 1
int k[N], b[N];
Node tr[N << 2];
// klx+bl krx+br -> kr(klx+bl)+br -> kl*kr x + kr*bl+br
Node operator+(Node l, Node r)
{Node u;u.k = l.k * r.k;u.b = r.k * l.b + r.b;return u;
}
void bud(int u, int l, int r)
{if (l == r){tr[u] = {k[l], b[l]};return;}int mid = l + r >> 1;bud(ls, l, mid), bud(rs, mid + 1, r);tr[u] = tr[ls] + tr[rs];
}
Node ask(int u, int l, int r, int ql, int qr)
{if (ql <= l && r <= qr)return tr[u];int mid = l + r >> 1;if (qr <= mid)return ask(ls, l, mid, ql, qr);else if (ql > mid)return ask(rs, mid + 1, r, ql, qr);return ask(ls, l, mid, ql, qr) + ask(rs, mid + 1, r, ql, qr);
}
void upd(int u, int l, int r, int tar, int nk, int nb)
{if (l == tar && tar == r){tr[u].k = nk, tr[u].b = nb;return;}int mid = l + r >> 1;if (tar <= mid)upd(ls, l, mid, tar, nk, nb);if (tar > mid)upd(rs, mid + 1, r, tar, nk, nb);tr[u] = tr[ls] + tr[rs];
}
16. 整除分块
    int l = 1, r = 0;while (l <= min(n, k)){r = k / (k / l);r = min(r, n);int ans = k / l * (l + r) * (r - l + 1) / 2;res -= ans;l = r + 1;}//二int l = 1, r = 0;for (; l <= n; l = r + 1){int cnt = n / l;r = n / cnt;if (cnt >= k)ans = max(ans, r * r * (1 + cnt) * cnt >> 1);}
17. 杂项
k 部分拆数 𝑝(𝑛,𝑘)=𝑝(𝑛−1,𝑘−1)+𝑝(𝑛−𝑘,𝑘)
互异分拆数 𝑝𝑑(𝑛,𝑘)=𝑝𝑑(𝑛−𝑘,𝑘−1)+𝑝𝑑(𝑛−𝑘,𝑘)三维前缀和
S[i][j][k] = S[i-1][j][k] + S[i][j-1][k] + S[i][j][k-1] - S[i-1][j-1][k] - S[i-1][j][k-1] - S[i][j-1][k-1] + S[i-1][j-1][k-1] + A[i][j][k]
sum = S[x2][y2][z2] - S[x1-1][y2][z2] - S[x2][y1-1][z2] - S[x2][y2][z1-1] + S[x1-1][y1-1][z2] + S[x1-1][y2][z1-1] + S[x2][y1-1][z1-1] - S[x1-1][y1-1][z1-1]每个合数n都有一个不大于√n的质因数。dp: 考虑前n个解、考虑且选择第n个的解  状态的设定需要易于状态的转移子字符串想前缀加哈希  子序列想dp题意需转化  正难则反dfs时只需要考虑局部过程(深度 分支)  返回值的使用认为能过却没过
1.前测试点 算法有漏洞-->重演 / 特殊样例(极小1 极大)  /  细节(long long 爆long long)
2.后测试点 正解+实现==AC
正向去做  考虑最终结果形态技巧
刷满分段的题,只想思路不去做,提升快
gcd与分解因数
n对 2^i 取模== (n 的二进制下低位中后 i位组成的数)思维
思想:本质 贡献 制约 转移 反向 答案 遍历 值域
方法:直接做/本质解法优化加速 写下解法
答案:答案假设则可能二分
结论的方式决定代码实现的难度:优化结论
优化加速的方式:数据结构优化 依托上一步结果
入手点:入手的方式,无则没有思路
关键点:做出的必要条件
巧妙点:优化实现方式
正向考虑太难,逆向思维从最后的答案观察 充分性与必要性
整体看太难,由小数据开始到大数据,由局部到总体
多组询问:预处理/预处理+log查询
在二进制下,lowbit(x)是 x 的一个因子。对数字组成分析,二进制是个必须的方向。
构造题考虑答案上限挺重要。算法
图:不连通 重边dfs_vis 根的度数也可为1
dfs:搜索树不用恢复现场不用vis 原图/数组恢复现场
树形dp:关注点:叶子和中间 操作:子节点和当前节点
树遍历子节点时传父节点即可 而图需要vis标记
gcd性质:考虑区间gcd时->前缀gcd断点log个
gcd(a,b,c,d,e)=gcd(a,b−a,c−b,d−c,e−d)
dp:专注于a[i]的取舍 分析a[i-1]与a[i]找到转移 判断转移的条件
优先本质[全面性] 抽象数据则具体化理解
dp最优子结构  逆向找 最后一次操作
贪心伴随排序 
18. 单hash

using u64 = unsigned long long;struct Hash
{u64 base = 1315423911;vector<u64> pow, h;Hash() {}Hash(const string &s) { init(s); }void init(const string &src){string s = " " + src;int N = s.size();pow.resize(N);h.resize(N);pow[0] = 1, h[0] = 0;for (int i = 1; i < N; i++){pow[i] = pow[i - 1] * base;h[i] = h[i - 1] * base + (u64)(unsigned char)s[i];}}u64 get(int l, int r) const{return h[r] - h[l - 1] * pow[r - l + 1];}bool same(int l1, int r1, int l2, int r2) const{return get(l1, r1) == get(l2, r2);}
};
19. Z函数
// 在字符串中高效地计算每个前缀与从某个位置开始的后缀的最长公共前缀长度。
// z[i] 表示字符串的前缀、s[i]开始的字符串 最长公共前缀长度// 在一个大序列中查找一个子序列模式(pattern matching);
// 计算最小周期、重复模式;
// 判断一个序列是否是另一个的循环移位;
// 快速比较前缀与某一段后缀的匹配程度
template <typename STRING> // string, vector
vector<int> zalgorithm(const STRING &s)
{int n = int(s.size());if (n == 0)return {};vector<int> z(n);z[0] = 0;for (int i = 1, j = 0; i < n; i++){int &k = z[i];k = (j + z[j] <= i) ? 0 : min(j + z[j] - i, z[i - j]);while (i + k < n && s[k] == s[i + k])k++;if (j + z[j] < i + z[i])j = i;}z[0] = n;return z;
}
http://www.dtcms.com/a/579441.html

相关文章:

  • discuz网站备份黔东南网站建设
  • 山西太原建站怎么做湖南沙坪建设集团有限公司网站
  • 襄阳网站建设公司怎样在手机上运行wordpress
  • 专业制作网站公司华强北附近网站建设
  • ctfshow-web入门-361-372-ssti
  • 网站建设与维护 东博wordpress sns插件
  • Linux LVM DR 模式部署实践
  • 西宁网站开发什么是网站建设与管理
  • 品牌加盟最好的网站建设扶贫网站建设的意义
  • 网站建设意思app维护费用一般多少钱
  • 自己用dw做网站能加声音吗没有网站如何做cpa推广
  • Nginx 的多个场景配置
  • 做网站要具备些什么条件steam怎么注册域名
  • Orangepi Zero2—全志H616开发
  • 荣成住房和城乡建设部网站在线教育网站设计
  • 成都网站网络建设广州网站推广找哪家
  • 那个网站有免费模板wordpress 聊天插件
  • 自建网站 支付宝做网站需求 后期方便优化
  • 做婚纱摄影网站做seo要投入什么
  • C++:类和对象---初级篇
  • 建立公司网站视频域名网查询
  • Linux(3)—— 权限操作
  • 网站系统建站1688创业商机网
  • 实时网站制作wordpress文件管理
  • STM32 采集差分编码器数据的方法
  • 预约网站制作网站空间数据丢失
  • 做网站即墨中国城乡住房和城乡建设部网站
  • 制作网站首页教案免费看网站源码
  • C语言字符串操作实战:手动实现strcpy与strcat函数
  • 开封网站建设-中企动力网页模板怎么设计