├── .github └── workflows │ └── main.yml ├── .gitignore ├── 0-一切的开始.md ├── 1-数据结构.md ├── 2-数学.md ├── 3-图论.md ├── 4-计算几何.md ├── 5-字符串.md ├── 6-杂项.md ├── assets └── sam.png ├── pandoc ├── algo.latex ├── gen.sh ├── minted.py └── readme.md └── readme.md /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions/setup-python@v1 16 | with: 17 | python-version: '3.x' # Version range or exact version of a Python version to use, using semver version range syntax. 18 | architecture: 'x64' # (x64 or x86) 19 | - name: Install Pandoc 20 | run: | 21 | mkdir -p /tmp/pandoc 22 | cd /tmp/pandoc 23 | python -m pip --no-cache-dir install setuptools wheel 24 | python -m pip --no-cache-dir install pandocfilters Pygments 25 | sudo apt install -y texlive texlive-xetex latexmk wget 26 | wget -q "https://github.com/jgm/pandoc/releases/download/2.7.3/pandoc-2.7.3-1-amd64.deb" 27 | sudo dpkg -i pandoc-2.7.3-1-amd64.deb 28 | rm -rf *.deb 29 | - name: Install Fonts 30 | run: | 31 | mkdir -p /tmp/adodefont 32 | cd /tmp/adodefont 33 | wget -q -O source-code-pro.zip https://github.com/adobe-fonts/source-code-pro/archive/2.030R-ro/1.050R-it.zip 34 | wget -q -O source-han-serif.zip https://github.com/adobe-fonts/source-han-serif/archive/1.001R.zip 35 | wget -q -O source-han-sans.zip https://github.com/adobe-fonts/source-han-sans/archive/2.001R.zip 36 | unzip -q source-code-pro.zip -d source-code-pro 37 | unzip -q source-han-serif.zip -d source-han-serif 38 | unzip -q source-han-sans.zip -d source-han-sans 39 | mkdir -p ~/.fonts 40 | cp -v source-code-pro/*/OTF/*.otf ~/.fonts/ 41 | cp -v source-han-serif/*/SubsetOTF/*/*.otf ~/.fonts/ 42 | cp -v source-han-sans/*/SubsetOTF/*/*.otf ~/.fonts/ 43 | fc-cache -f 44 | rm -rf source-code-pro{,.zip} source-han-serif{,.zip} source-han-sans{,.zip} 45 | - name: Generate PDF 46 | run: | 47 | mkdir -p ~/.pandoc/templates 48 | cp pandoc/algo.latex ~/.pandoc/templates/ 49 | sh pandoc/gen.sh 50 | - name: Deploy 51 | run: | 52 | git clone -b gh-pages https://oauth2:${{secrets.ACCESS_TOKEN}}@github.com/F0RE1GNERS/template /tmp/template 53 | cp template.pdf /tmp/template/ 54 | cd /tmp/template 55 | git add template.pdf 56 | git config --global user.email "bot@foreigners.io" 57 | git config --global user.name "F0RE1GNERS BOT" 58 | git commit -m "Auto-generated PDF commit" 59 | git push 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | template.pdf 2 | WF-Team-Reference-Document.pdf 3 | .idea/ 4 | -------------------------------------------------------------------------------- /0-一切的开始.md: -------------------------------------------------------------------------------- 1 | # 一切的开始 2 | 3 | ## 宏定义 4 | 5 | * 需要 C++11 6 | 7 | ```cpp 8 | #include 9 | using namespace std; 10 | using LL = long long; 11 | #define FOR(i, x, y) for (decay::type i = (x), _##i = (y); i < _##i; ++i) 12 | #define FORD(i, x, y) for (decay::type i = (x), _##i = (y); i > _##i; --i) 13 | #ifdef zerol 14 | #define dbg(x...) do { cout << "\033[32;1m" << #x << " -> "; err(x); } while (0) 15 | void err() { cout << "\033[39;0m" << endl; } 16 | template class T, typename t, typename... A> 17 | void err(T a, A... x) { for (auto v: a) cout << v << ' '; err(x...); } 18 | template 19 | void err(T a, A... x) { cout << a << ' '; err(x...); } 20 | #else 21 | #define dbg(...) 22 | #endif 23 | // ----------------------------------------------------------------------------- 24 | ``` 25 | 26 | + 更多配色: 27 | + 33 黄色 28 | + 34 蓝色 29 | + 31 橙色 30 | 31 | + POJ/BZOJ version 32 | 33 | ```cpp 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | using namespace std; 45 | typedef long long LL; 46 | #define FOR(i, x, y) for (LL i = (x), _##i = (y); i < _##i; ++i) 47 | #define FORD(i, x, y) for (LL i = (x), _##i = (y); i > _##i; --i) 48 | #ifdef zerol 49 | #define dbg(args...) do { cout << "\033[32;1m" << #args<< " -> "; err(args); } while (0) 50 | void err() { cout << "\033[39;0m" << endl; } 51 | template 52 | void err(T a, Args... args) { cout << a << ' '; err(args...); } 53 | #else 54 | #define dbg(...) 55 | #endif 56 | // ----------------------------------------------------------------------------- 57 | ``` 58 | 59 | + CMakeLists.txt (for CLion) 60 | 61 | ```cmake 62 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Dzerol -Wall") 63 | ``` 64 | 65 | 66 | + HDU Assert Patch 67 | 68 | ```cpp 69 | #ifdef ONLINE_JUDGE 70 | #define assert(condition) do if (!condition) exit(*(int*)0); while (0) 71 | #endif 72 | ``` 73 | 74 | ## 快速读 75 | 76 | ```cpp 77 | inline char nc() { 78 | static char buf[100000], *p1 = buf, *p2 = buf; 79 | return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++; 80 | } 81 | template 82 | bool rn(T& v) { 83 | static char ch; 84 | while (ch != EOF && !isdigit(ch)) ch = nc(); 85 | if (ch == EOF) return false; 86 | for (v = 0; isdigit(ch); ch = nc()) 87 | v = v * 10 + ch - '0'; 88 | return true; 89 | } 90 | 91 | template 92 | void o(T p) { 93 | static int stk[70], tp; 94 | if (p == 0) { putchar('0'); return; } 95 | if (p < 0) { p = -p; putchar('-'); } 96 | while (p) stk[++tp] = p % 10, p /= 10; 97 | while (tp) putchar(stk[tp--] + '0'); 98 | } 99 | ``` 100 | 101 | 102 | + 需要初始化 103 | + 需要一次读入 104 | + 不支持负数 105 | 106 | ```cpp 107 | const int MAXS = 100 * 1024 * 1024; 108 | char buf[MAXS]; 109 | template 110 | inline bool read(T& x) { 111 | static char* p = buf; 112 | x = 0; 113 | while (*p && !isdigit(*p)) ++p; 114 | if (!*p) return false; 115 | while (isdigit(*p)) x = x * 10 + *p++ - 48; 116 | return true; 117 | } 118 | 119 | fread(buf, 1, MAXS, stdin); 120 | ``` 121 | 122 | ## 对拍 123 | 124 | ```bash 125 | #!/usr/bin/env bash 126 | g++ -o r main.cpp -O2 -std=c++11 127 | g++ -o std std.cpp -O2 -std=c++11 128 | while true; do 129 | python gen.py > in 130 | ./std < in > stdout 131 | ./r < in > out 132 | if test $? -ne 0; then 133 | exit 0 134 | fi 135 | if diff stdout out; then 136 | printf "AC\n" 137 | else 138 | printf "GG\n" 139 | exit 0 140 | fi 141 | done 142 | ``` 143 | 144 | + 快速编译运行 (配合无插件 VSC) 145 | 146 | ```bash 147 | #!/bin/bash 148 | g++ $1.cpp -o $1 -O2 -std=c++14 -Wall -Dzerol -g 149 | if $? -eq 0; then 150 | ./$1 151 | fi 152 | ``` 153 | 154 | 155 | 156 | ## 为什么 C++ 不自带这个? 157 | 158 | ```cpp 159 | LL bin(LL x, LL n, LL MOD) { 160 | LL ret = MOD != 1; 161 | for (x %= MOD; n; n >>= 1, x = x * x % MOD) 162 | if (n & 1) ret = ret * x % MOD; 163 | return ret; 164 | } 165 | inline LL get_inv(LL x, LL p) { return bin(x, p - 2, p); } 166 | ``` 167 | 168 | -------------------------------------------------------------------------------- /1-数据结构.md: -------------------------------------------------------------------------------- 1 | # 数据结构 2 | 3 | ## ST 表 4 | 5 | + 二维 6 | 7 | ```cpp 8 | int f[maxn][maxn][10][10]; 9 | inline int highbit(int x) { return 31 - __builtin_clz(x); } 10 | inline int calc(int x, int y, int xx, int yy, int p, int q) { 11 | return max( 12 | max(f[x][y][p][q], f[xx - (1 << p) + 1][yy - (1 << q) + 1][p][q]), 13 | max(f[xx - (1 << p) + 1][y][p][q], f[x][yy - (1 << q) + 1][p][q]) 14 | ); 15 | } 16 | void init() { 17 | FOR (x, 0, highbit(n) + 1) 18 | FOR (y, 0, highbit(m) + 1) 19 | FOR (i, 0, n - (1 << x) + 1) 20 | FOR (j, 0, m - (1 << y) + 1) { 21 | if (!x && !y) { f[i][j][x][y] = a[i][j]; continue; } 22 | f[i][j][x][y] = calc( 23 | i, j, 24 | i + (1 << x) - 1, j + (1 << y) - 1, 25 | max(x - 1, 0), max(y - 1, 0) 26 | ); 27 | } 28 | } 29 | inline int get_max(int x, int y, int xx, int yy) { 30 | return calc(x, y, xx, yy, highbit(xx - x + 1), highbit(yy - y + 1)); 31 | } 32 | ``` 33 | 34 | + 一维 35 | 36 | ```cpp 37 | struct RMQ { 38 | int f[22][M]; 39 | inline int highbit(int x) { return 31 - __builtin_clz(x); } 40 | void init(int* v, int n) { 41 | FOR (i, 0, n) f[0][i] = v[i]; 42 | FOR (x, 1, highbit(n) + 1) 43 | FOR (i, 0, n - (1 << x) + 1) 44 | f[x][i] = min(f[x - 1][i], f[x - 1][i + (1 << (x - 1))]); 45 | } 46 | int get_min(int l, int r) { 47 | assert(l <= r); 48 | int t = highbit(r - l + 1); 49 | return min(f[t][l], f[t][r - (1 << t) + 1]); 50 | } 51 | } rmq; 52 | ``` 53 | 54 | ## 线段树 55 | 56 | + 普适 57 | 58 | ```cpp 59 | namespace sg { 60 | struct Q { 61 | LL setv; 62 | explicit Q(LL setv = -1): setv(setv) {} 63 | void operator += (const Q& q) { if (q.setv != -1) setv = q.setv; } 64 | }; 65 | struct P { 66 | LL min; 67 | explicit P(LL min = INF): min(min) {} 68 | void up(Q& q) { if (q.setv != -1) min = q.setv; } 69 | }; 70 | template 71 | P operator & (T&& a, T&& b) { 72 | return P(min(a.min, b.min)); 73 | } 74 | P p[maxn << 2]; 75 | Q q[maxn << 2]; 76 | #define lson o * 2, l, (l + r) / 2 77 | #define rson o * 2 + 1, (l + r) / 2 + 1, r 78 | void up(int o, int l, int r) { 79 | if (l == r) p[o] = P(); 80 | else p[o] = p[o * 2] & p[o * 2 + 1]; 81 | p[o].up(q[o]); 82 | } 83 | void down(int o, int l, int r) { 84 | q[o * 2] += q[o]; q[o * 2 + 1] += q[o]; 85 | q[o] = Q(); 86 | up(lson); up(rson); 87 | } 88 | template 89 | void build(T&& f, int o = 1, int l = 1, int r = n) { 90 | if (l == r) q[o] = f(l); 91 | else { build(f, lson); build(f, rson); q[o] = Q(); } 92 | up(o, l, r); 93 | } 94 | P query(int ql, int qr, int o = 1, int l = 1, int r = n) { 95 | if (ql > r || l > qr) return P(); 96 | if (ql <= l && r <= qr) return p[o]; 97 | down(o, l, r); 98 | return query(ql, qr, lson) & query(ql, qr, rson); 99 | } 100 | void update(int ql, int qr, const Q& v, int o = 1, int l = 1, int r = n) { 101 | if (ql > r || l > qr) return; 102 | if (ql <= l && r <= qr) q[o] += v; 103 | else { 104 | down(o, l, r); 105 | update(ql, qr, v, lson); update(ql, qr, v, rson); 106 | } 107 | up(o, l, r); 108 | } 109 | } 110 | ``` 111 | 112 | + SET + ADD 113 | 114 | ```cpp 115 | struct IntervalTree { 116 | #define ls o * 2, l, m 117 | #define rs o * 2 + 1, m + 1, r 118 | static const LL M = maxn * 4, RS = 1E18 - 1; 119 | LL addv[M], setv[M], minv[M], maxv[M], sumv[M]; 120 | void init() { 121 | memset(addv, 0, sizeof addv); 122 | fill(setv, setv + M, RS); 123 | memset(minv, 0, sizeof minv); 124 | memset(maxv, 0, sizeof maxv); 125 | memset(sumv, 0, sizeof sumv); 126 | } 127 | void maintain(LL o, LL l, LL r) { 128 | if (l < r) { 129 | LL lc = o * 2, rc = o * 2 + 1; 130 | sumv[o] = sumv[lc] + sumv[rc]; 131 | minv[o] = min(minv[lc], minv[rc]); 132 | maxv[o] = max(maxv[lc], maxv[rc]); 133 | } else sumv[o] = minv[o] = maxv[o] = 0; 134 | if (setv[o] != RS) { minv[o] = maxv[o] = setv[o]; sumv[o] = setv[o] * (r - l + 1); } 135 | if (addv[o]) { minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += addv[o] * (r - l + 1); } 136 | } 137 | void build(LL o, LL l, LL r) { 138 | if (l == r) addv[o] = a[l]; 139 | else { 140 | LL m = (l + r) / 2; 141 | build(ls); build(rs); 142 | } 143 | maintain(o, l, r); 144 | } 145 | void pushdown(LL o) { 146 | LL lc = o * 2, rc = o * 2 + 1; 147 | if (setv[o] != RS) { 148 | setv[lc] = setv[rc] = setv[o]; 149 | addv[lc] = addv[rc] = 0; 150 | setv[o] = RS; 151 | } 152 | if (addv[o]) { 153 | addv[lc] += addv[o]; addv[rc] += addv[o]; 154 | addv[o] = 0; 155 | } 156 | } 157 | void update(LL p, LL q, LL o, LL l, LL r, LL v, LL op) { 158 | if (p <= r && l <= q) 159 | if (p <= l && r <= q) { 160 | if (op == 2) { setv[o] = v; addv[o] = 0; } 161 | else addv[o] += v; 162 | } else { 163 | pushdown(o); 164 | LL m = (l + r) / 2; 165 | update(p, q, ls, v, op); update(p, q, rs, v, op); 166 | } 167 | maintain(o, l, r); 168 | } 169 | void query(LL p, LL q, LL o, LL l, LL r, LL add, LL& ssum, LL& smin, LL& smax) { 170 | if (p > r || l > q) return; 171 | if (setv[o] != RS) { 172 | LL v = setv[o] + add + addv[o]; 173 | ssum += v * (min(r, q) - max(l, p) + 1); 174 | smin = min(smin, v); 175 | smax = max(smax, v); 176 | } else if (p <= l && r <= q) { 177 | ssum += sumv[o] + add * (r - l + 1); 178 | smin = min(smin, minv[o] + add); 179 | smax = max(smax, maxv[o] + add); 180 | } else { 181 | LL m = (l + r) / 2; 182 | query(p, q, ls, add + addv[o], ssum, smin, smax); 183 | query(p, q, rs, add + addv[o], ssum, smin, smax); 184 | } 185 | } 186 | } IT; 187 | ``` 188 | 189 | ## 均摊复杂度线段树 190 | 191 | + 区间取 min,区间求和。 192 | 193 | ```cpp 194 | namespace R { 195 | #define lson o * 2, l, (l + r) / 2 196 | #define rson o * 2 + 1, (l + r) / 2 + 1, r 197 | int m1[N], m2[N], cm1[N]; 198 | LL sum[N]; 199 | void up(int o) { 200 | int lc = o * 2, rc = lc + 1; 201 | m1[o] = max(m1[lc], m1[rc]); 202 | sum[o] = sum[lc] + sum[rc]; 203 | if (m1[lc] == m1[rc]) { 204 | cm1[o] = cm1[lc] + cm1[rc]; 205 | m2[o] = max(m2[lc], m2[rc]); 206 | } else { 207 | cm1[o] = m1[lc] > m1[rc] ? cm1[lc] : cm1[rc]; 208 | m2[o] = max(min(m1[lc], m1[rc]), max(m2[lc], m2[rc])); 209 | } 210 | } 211 | void mod(int o, int x) { 212 | if (x >= m1[o]) return; 213 | assert(x > m2[o]); 214 | sum[o] -= 1LL * (m1[o] - x) * cm1[o]; 215 | m1[o] = x; 216 | } 217 | void down(int o) { 218 | int lc = o * 2, rc = lc + 1; 219 | mod(lc, m1[o]); mod(rc, m1[o]); 220 | } 221 | void build(int o, int l, int r) { 222 | if (l == r) { int t; read(t); sum[o] = m1[o] = t; m2[o] = -INF; cm1[o] = 1; } 223 | else { build(lson); build(rson); up(o); } 224 | } 225 | void update(int ql, int qr, int x, int o, int l, int r) { 226 | if (r < ql || qr < l || m1[o] <= x) return; 227 | if (ql <= l && r <= qr && m2[o] < x) { mod(o, x); return; } 228 | down(o); 229 | update(ql, qr, x, lson); update(ql, qr, x, rson); 230 | up(o); 231 | } 232 | int qmax(int ql, int qr, int o, int l, int r) { 233 | if (r < ql || qr < l) return -INF; 234 | if (ql <= l && r <= qr) return m1[o]; 235 | down(o); 236 | return max(qmax(ql, qr, lson), qmax(ql, qr, rson)); 237 | } 238 | LL qsum(int ql, int qr, int o, int l, int r) { 239 | if (r < ql || qr < l) return 0; 240 | if (ql <= l && r <= qr) return sum[o]; 241 | down(o); 242 | return qsum(ql, qr, lson) + qsum(ql, qr, rson); 243 | } 244 | } 245 | ``` 246 | 247 | 248 | 249 | ## 持久化线段树 250 | 251 | + ADD 252 | 253 | ```cpp 254 | namespace tree { 255 | #define mid ((l + r) >> 1) 256 | #define lson ql, qr, l, mid 257 | #define rson ql, qr, mid + 1, r 258 | struct P { 259 | LL add, sum; 260 | int ls, rs; 261 | } tr[maxn * 45 * 2]; 262 | int sz = 1; 263 | int N(LL add, int l, int r, int ls, int rs) { 264 | tr[sz] = {add, tr[ls].sum + tr[rs].sum + add * (len[r] - len[l - 1]), ls, rs}; 265 | return sz++; 266 | } 267 | int update(int o, int ql, int qr, int l, int r, LL add) { 268 | if (ql > r || l > qr) return o; 269 | const P& t = tr[o]; 270 | if (ql <= l && r <= qr) return N(add + t.add, l, r, t.ls, t.rs); 271 | return N(t.add, l, r, update(t.ls, lson, add), update(t.rs, rson, add)); 272 | } 273 | LL query(int o, int ql, int qr, int l, int r, LL add = 0) { 274 | if (ql > r || l > qr) return 0; 275 | const P& t = tr[o]; 276 | if (ql <= l && r <= qr) return add * (len[r] - len[l - 1]) + t.sum; 277 | return query(t.ls, lson, add + t.add) + query(t.rs, rson, add + t.add); 278 | } 279 | } 280 | ``` 281 | 282 | ## K-D Tree 283 | 284 | **最优化问题一定要用全局变量大力剪枝,而且左右儿子先递归潜力大的** 285 | 286 | + 维护信息 287 | + 带重构(适合在线) 288 | + 插入时左右儿子要标记为 `null`。 289 | 290 | ```cpp 291 | namespace kd { 292 | const int K = 2, inf = 1E9, M = N; 293 | const double lim = 0.7; 294 | struct P { 295 | int d[K], l[K], r[K], sz, val; 296 | LL sum; 297 | P *ls, *rs; 298 | P* up() { 299 | sz = ls->sz + rs->sz + 1; 300 | sum = ls->sum + rs->sum + val; 301 | FOR (i, 0, K) { 302 | l[i] = min(d[i], min(ls->l[i], rs->l[i])); 303 | r[i] = max(d[i], max(ls->r[i], rs->r[i])); 304 | } 305 | return this; 306 | } 307 | } pool[M], *null = new P, *pit = pool; 308 | static P *tmp[M], **pt; 309 | void init() { 310 | null->ls = null->rs = null; 311 | FOR (i, 0, K) null->l[i] = inf, null->r[i] = -inf; 312 | null->sum = null->val = 0; 313 | null->sz = 0; 314 | } 315 | 316 | P* build(P** l, P** r, int d = 0) { // [l, r) 317 | if (d == K) d = 0; 318 | if (l >= r) return null; 319 | P** m = l + (r - l) / 2; assert(l <= m && m < r); 320 | nth_element(l, m, r, [&](const P* a, const P* b){ 321 | return a->d[d] < b->d[d]; 322 | }); 323 | P* o = *m; 324 | o->ls = build(l, m, d + 1); o->rs = build(m + 1, r, d + 1); 325 | return o->up(); 326 | } 327 | P* Build() { 328 | pt = tmp; FOR (it, pool, pit) *pt++ = it; 329 | return build(tmp, pt); 330 | } 331 | inline bool inside(int p[], int q[], int l[], int r[]) { 332 | FOR (i, 0, K) if (r[i] < q[i] || p[i] < l[i]) return false; 333 | return true; 334 | } 335 | LL query(P* o, int l[], int r[]) { 336 | if (o == null) return 0; 337 | FOR (i, 0, K) if (o->r[i] < l[i] || r[i] < o->l[i]) return 0; 338 | if (inside(o->l, o->r, l, r)) return o->sum; 339 | return query(o->ls, l, r) + query(o->rs, l, r) + 340 | (inside(o->d, o->d, l, r) ? o->val : 0); 341 | } 342 | void dfs(P* o) { 343 | if (o == null) return; 344 | *pt++ = o; dfs(o->ls); dfs(o->rs); 345 | } 346 | P* ins(P* o, P* x, int d = 0) { 347 | if (d == K) d = 0; 348 | if (o == null) return x->up(); 349 | P*& oo = x->d[d] <= o->d[d] ? o->ls : o->rs; 350 | if (oo->sz > o->sz * lim) { 351 | pt = tmp; dfs(o); *pt++ = x; 352 | return build(tmp, pt, d); 353 | } 354 | oo = ins(oo, x, d + 1); 355 | return o->up(); 356 | } 357 | } 358 | ``` 359 | 360 | + 维护信息 361 | + 带修改(适合离线) 362 | 363 | ```cpp 364 | namespace kd { 365 | const int K = 3, inf = 1E9, M = N << 3; 366 | extern struct P* null; 367 | struct P { 368 | int d[K], l[K], r[K], val; 369 | int Max; 370 | P *ls, *rs, *fa; 371 | P* up() { 372 | Max = max(val, max(ls->Max, rs->Max)); 373 | FOR (i, 0, K) { 374 | l[i] = min(d[i], min(ls->l[i], rs->l[i])); 375 | r[i] = max(d[i], max(ls->r[i], rs->r[i])); 376 | } 377 | return ls->fa = rs->fa = this; 378 | } 379 | } pool[M], *null = new P, *pit = pool; 380 | void upd(P* o, int val) { 381 | o->val = val; 382 | for (; o != null; o = o->fa) 383 | o->Max = max(o->Max, val); 384 | } 385 | static P *tmp[M], **pt; 386 | void init() { 387 | null->ls = null->rs = null; 388 | FOR (i, 0, K) null->l[i] = inf, null->r[i] = -inf; 389 | null->Max = null->val = 0; 390 | } 391 | P* build(P** l, P** r, int d = 0) { // [l, r) 392 | if (d == K) d = 0; 393 | if (l >= r) return null; 394 | P** m = l + (r - l) / 2; assert(l <= m && m < r); 395 | nth_element(l, m, r, [&](const P* a, const P* b){ 396 | return a->d[d] < b->d[d]; 397 | }); 398 | P* o = *m; 399 | o->ls = build(l, m, d + 1); o->rs = build(m + 1, r, d + 1); 400 | return o->up(); 401 | } 402 | P* Build() { 403 | pt = tmp; FOR (it, pool, pit) *pt++ = it; 404 | P* ret = build(tmp, pt); ret->fa = null; 405 | return ret; 406 | } 407 | inline bool inside(int p[], int q[], int l[], int r[]) { 408 | FOR (i, 0, K) if (r[i] < q[i] || p[i] < l[i]) return false; 409 | return true; 410 | } 411 | int query(P* o, int l[], int r[]) { 412 | if (o == null) return 0; 413 | FOR (i, 0, K) if (o->r[i] < l[i] || r[i] < o->l[i]) return 0; 414 | if (inside(o->l, o->r, l, r)) return o->Max; 415 | int ret = 0; 416 | if (o->val > ret && inside(o->d, o->d, l, r)) ret = max(ret, o->val); 417 | if (o->ls->Max > ret) ret = max(ret, query(o->ls, l, r)); 418 | if (o->rs->Max > ret) ret = max(ret, query(o->rs, l, r)); 419 | return ret; 420 | } 421 | } 422 | ``` 423 | 424 | + 最近点对 425 | + 要用全局变量大力剪枝 426 | 427 | ```cpp 428 | namespace kd { 429 | const int K = 3; 430 | const int M = N; 431 | const int inf = 1E9 + 100; 432 | struct P { 433 | int d[K]; 434 | int l[K], r[K]; 435 | P *ls, *rs; 436 | P* up() { 437 | FOR (i, 0, K) { 438 | l[i] = min(d[i], min(ls->l[i], rs->l[i])); 439 | r[i] = max(d[i], max(ls->r[i], rs->r[i])); 440 | } 441 | return this; 442 | } 443 | } pool[M], *null = new P, *pit = pool; 444 | static P *tmp[M], **pt; 445 | void init() { 446 | null->ls = null->rs = null; 447 | FOR (i, 0, K) null->l[i] = inf, null->r[i] = -inf; 448 | } 449 | P* build(P** l, P** r, int d = 0) { // [l, r) 450 | if (d == K) d = 0; 451 | if (l >= r) return null; 452 | P** m = l + (r - l) / 2; 453 | nth_element(l, m, r, [&](const P* a, const P* b){ 454 | return a->d[d] < b->d[d]; 455 | }); 456 | P* o = *m; 457 | o->ls = build(l, m, d + 1); o->rs = build(m + 1, r, d + 1); 458 | return o->up(); 459 | } 460 | LL eval(P* o, int d[]) { 461 | // ... 462 | } 463 | LL dist(int d1[], int d2[]) { 464 | // ... 465 | } 466 | LL S; 467 | LL query(P* o, int d[]) { 468 | if (o == null) return 0; 469 | S = max(S, dist(o->d, d)); 470 | LL mdl = eval(o->ls, d), mdr = eval(o->rs, d); 471 | if (mdl < mdr) { 472 | if (S > mdl) S = max(S, query(o->ls, d)); 473 | if (S > mdr) S = max(S, query(o->rs, d)); 474 | } else { 475 | if (S > mdr) S = max(S, query(o->rs, d)); 476 | if (S > mdl) S = max(S, query(o->ls, d)); 477 | } 478 | return S; 479 | } 480 | P* Build() { 481 | pt = tmp; FOR (it, pool, pit) *pt++ = it; 482 | return build(tmp, pt); 483 | } 484 | } 485 | ``` 486 | 487 | 488 | 489 | ## 树状数组 490 | 491 | * 注意:0 是无效下标 492 | 493 | ```cpp 494 | namespace bit { 495 | LL c[M]; 496 | inline int lowbit(int x) { return x & -x; } 497 | void add(int x, LL v) { 498 | for (int i = x; i < M; i += lowbit(i)) 499 | c[i] += v; 500 | } 501 | LL sum(int x) { 502 | LL ret = 0; 503 | for (int i = x; i > 0; i -= lowbit(i)) 504 | ret += c[i]; 505 | return ret; 506 | } 507 | int kth(LL k) { 508 | int p = 0; 509 | for (int lim = 1 << 20; lim; lim /= 2) 510 | if (p + lim < M && c[p + lim] < k) { 511 | p += lim; 512 | k -= c[p]; 513 | } 514 | return p + 1; 515 | } 516 | LL sum(int l, int r) { return sum(r) - sum(l - 1); } 517 | void add(int l, int r, LL v) { add(l, v); add(r + 1, -v); } 518 | } 519 | ``` 520 | 521 | + 区间修改 & 区间查询(单点修改,查询前缀和的前缀和) 522 | 523 | ```cpp 524 | namespace bit { 525 | int c[maxn], cc[maxn]; 526 | inline int lowbit(int x) { return x & -x; } 527 | void add(int x, int v) { 528 | for (int i = x; i <= n; i += lowbit(i)) { 529 | c[i] += v; cc[i] += x * v; 530 | } 531 | } 532 | void add(int l, int r, int v) { add(l, v); add(r + 1, -v); } 533 | int sum(int x) { 534 | int ret = 0; 535 | for (int i = x; i > 0; i -= lowbit(i)) 536 | ret += (x + 1) * c[i] - cc[i]; 537 | return ret; 538 | } 539 | int sum(int l, int r) { return sum(r) - sum(l - 1); } 540 | } 541 | ``` 542 | 543 | + 单点修改,查询前缀和的前缀和的前缀和(有用才怪) 544 | 545 | ```cpp 546 | namespace bit { 547 | LL c[N], cc[N], ccc[N]; 548 | inline LL lowbit(LL x) { return x & -x; } 549 | void add(LL x, LL v) { 550 | for (LL i = x; i < N; i += lowbit(i)) { 551 | c[i] = (c[i] + v) % MOD; 552 | cc[i] = (cc[i] + x * v) % MOD; 553 | ccc[i] = (ccc[i] + x * x % MOD * v) % MOD; 554 | } 555 | } 556 | void add(LL l, LL r, LL v) { add(l, v); add(r + 1, -v); } 557 | LL sum(LL x) { 558 | static LL INV2 = (MOD + 1) / 2; 559 | LL ret = 0; 560 | for (LL i = x; i > 0; i -= lowbit(i)) 561 | ret += (x + 1) * (x + 2) % MOD * c[i] % MOD 562 | - (2 * x + 3) * cc[i] % MOD 563 | + ccc[i]; 564 | return ret % MOD * INV2 % MOD; 565 | } 566 | LL sum(LL l, LL r) { return sum(r) - sum(l - 1); } 567 | } 568 | 569 | ``` 570 | 571 | + 三维 572 | 573 | ```cpp 574 | inline int lowbit(int x) { return x & -x; } 575 | void update(int x, int y, int z, int d) { 576 | for (int i = x; i <= n; i += lowbit(i)) 577 | for (int j = y; j <= n; j += lowbit(j)) 578 | for (int k = z; k <= n; k += lowbit(k)) 579 | c[i][j][k] += d; 580 | } 581 | LL query(int x, int y, int z) { 582 | LL ret = 0; 583 | for (int i = x; i > 0; i -= lowbit(i)) 584 | for (int j = y; j > 0; j -= lowbit(j)) 585 | for (int k = z; k > 0; k -= lowbit(k)) 586 | ret += c[i][j][k]; 587 | return ret; 588 | } 589 | LL solve(int x, int y, int z, int xx, int yy, int zz) { 590 | return query(xx, yy, zz) 591 | - query(xx, yy, z - 1) 592 | - query(xx, y - 1, zz) 593 | - query(x - 1, yy, zz) 594 | + query(xx, y - 1, z - 1) 595 | + query(x - 1, yy, z - 1) 596 | + query(x - 1, y - 1, zz) 597 | - query(x - 1, y - 1, z - 1); 598 | ``` 599 | 600 | ## 主席树 601 | 602 | + 正常主席树 603 | 604 | 605 | ```cpp 606 | namespace tree { 607 | #define mid ((l + r) >> 1) 608 | #define lson l, mid 609 | #define rson mid + 1, r 610 | const int MAGIC = M * 30; 611 | struct P { 612 | int sum, ls, rs; 613 | } tr[MAGIC] = {{0, 0, 0}}; 614 | int sz = 1; 615 | int N(int sum, int ls, int rs) { 616 | if (sz == MAGIC) assert(0); 617 | tr[sz] = {sum, ls, rs}; 618 | return sz++; 619 | } 620 | int ins(int o, int x, int v, int l = 1, int r = ls) { 621 | if (x < l || x > r) return o; 622 | const P& t = tr[o]; 623 | if (l == r) return N(t.sum + v, 0, 0); 624 | return N(t.sum + v, ins(t.ls, x, v, lson), ins(t.rs, x, v, rson)); 625 | } 626 | int query(int o, int ql, int qr, int l = 1, int r = ls) { 627 | if (ql > r || l > qr) return 0; 628 | const P& t = tr[o]; 629 | if (ql <= l && r <= qr) return t.sum; 630 | return query(t.ls, ql, qr, lson) + query(t.rs, ql, qr, rson); 631 | } 632 | } 633 | ``` 634 | 635 | + 第 k 大 636 | 637 | ```cpp 638 | struct TREE { 639 | #define mid ((l + r) >> 1) 640 | #define lson l, mid 641 | #define rson mid + 1, r 642 | struct P { 643 | int w, ls, rs; 644 | } tr[maxn * 20]; 645 | int sz = 1; 646 | TREE() { tr[0] = {0, 0, 0}; } 647 | int N(int w, int ls, int rs) { 648 | tr[sz] = {w, ls, rs}; 649 | return sz++; 650 | } 651 | int ins(int tt, int l, int r, int x) { 652 | if (x < l || r < x) return tt; 653 | const P& t = tr[tt]; 654 | if (l == r) return N(t.w + 1, 0, 0); 655 | return N(t.w + 1, ins(t.ls, lson, x), ins(t.rs, rson, x)); 656 | } 657 | int query(int pp, int qq, int l, int r, int k) { // (pp, qq] 658 | if (l == r) return l; 659 | const P &p = tr[pp], &q = tr[qq]; 660 | int w = tr[q.ls].w - tr[p.ls].w; 661 | if (k <= w) return query(p.ls, q.ls, lson, k); 662 | else return query(p.rs, q.rs, rson, k - w); 663 | } 664 | } tree; 665 | ``` 666 | 667 | + 树状数组套主席树 668 | 669 | ```cpp 670 | typedef vector VI; 671 | struct TREE { 672 | #define mid ((l + r) >> 1) 673 | #define lson l, mid 674 | #define rson mid + 1, r 675 | struct P { 676 | int w, ls, rs; 677 | } tr[maxn * 20 * 20]; 678 | int sz = 1; 679 | TREE() { tr[0] = {0, 0, 0}; } 680 | int N(int w, int ls, int rs) { 681 | tr[sz] = {w, ls, rs}; 682 | return sz++; 683 | } 684 | int add(int tt, int l, int r, int x, int d) { 685 | if (x < l || r < x) return tt; 686 | const P& t = tr[tt]; 687 | if (l == r) return N(t.w + d, 0, 0); 688 | return N(t.w + d, add(t.ls, lson, x, d), add(t.rs, rson, x, d)); 689 | } 690 | int ls_sum(const VI& rt) { 691 | int ret = 0; 692 | FOR (i, 0, rt.size()) 693 | ret += tr[tr[rt[i]].ls].w; 694 | return ret; 695 | } 696 | inline void ls(VI& rt) { transform(rt.begin(), rt.end(), rt.begin(), [&](int x)->int{ return tr[x].ls; }); } 697 | inline void rs(VI& rt) { transform(rt.begin(), rt.end(), rt.begin(), [&](int x)->int{ return tr[x].rs; }); } 698 | int query(VI& p, VI& q, int l, int r, int k) { 699 | if (l == r) return l; 700 | int w = ls_sum(q) - ls_sum(p); 701 | if (k <= w) { 702 | ls(p); ls(q); 703 | return query(p, q, lson, k); 704 | } 705 | else { 706 | rs(p); rs(q); 707 | return query(p, q, rson, k - w); 708 | } 709 | } 710 | } tree; 711 | struct BIT { 712 | int root[maxn]; 713 | void init() { memset(root, 0, sizeof root); } 714 | inline int lowbit(int x) { return x & -x; } 715 | void update(int p, int x, int d) { 716 | for (int i = p; i <= m; i += lowbit(i)) 717 | root[i] = tree.add(root[i], 1, m, x, d); 718 | } 719 | int query(int l, int r, int k) { 720 | VI p, q; 721 | for (int i = l - 1; i > 0; i -= lowbit(i)) p.push_back(root[i]); 722 | for (int i = r; i > 0; i -= lowbit(i)) q.push_back(root[i]); 723 | return tree.query(p, q, 1, m, k); 724 | } 725 | } bit; 726 | 727 | void init() { 728 | m = 10000; 729 | tree.sz = 1; 730 | bit.init(); 731 | FOR (i, 1, m + 1) 732 | bit.update(i, a[i], 1); 733 | } 734 | ``` 735 | 736 | ## 左偏树 737 | 738 | ```cpp 739 | namespace LTree { 740 | extern struct P* null, *pit; 741 | queue trash; 742 | const int M = 1E5 + 100; 743 | struct P { 744 | P *ls, *rs; 745 | LL v; 746 | int d; 747 | void operator delete (void* ptr) { 748 | trash.push((P*)ptr); 749 | } 750 | void* operator new(size_t size) { 751 | if (trash.empty()) return pit++; 752 | void* ret = trash.front(); trash.pop(); return ret; 753 | } 754 | 755 | void prt() { 756 | if (this == null) return; 757 | cout << v << ' '; 758 | ls->prt(); rs->prt(); 759 | } 760 | } pool[M], *pit = pool, *null = new P{0, 0, -1, -1}; 761 | P* N(LL v) { 762 | return new P{null, null, v, 0}; 763 | } 764 | P* merge(P* a, P* b) { 765 | if (a == null) return b; 766 | if (b == null) return a; 767 | if (a->v > b->v) swap(a, b); 768 | a->rs = merge(a->rs, b); 769 | if (a->ls->d < a->rs->d) swap(a->ls, a->rs); 770 | a->d = a->rs->d + 1; 771 | return a; 772 | } 773 | 774 | LL pop(P*& o) { 775 | LL ret = o->v; 776 | P* t = o; 777 | o = merge(o->ls, o->rs); 778 | delete t; 779 | return ret; 780 | } 781 | } 782 | ``` 783 | 784 | 可持久化 785 | 786 | ```cpp 787 | namespace LTree { 788 | extern struct P* null, *pit; 789 | queue trash; 790 | const int M = 1E6 + 100; 791 | struct P { 792 | P *ls, *rs; 793 | LL v; 794 | int d; 795 | void operator delete (void* ptr) { 796 | trash.push((P*)ptr); 797 | } 798 | void* operator new(size_t size) { 799 | if (trash.empty()) return pit++; 800 | void* ret = trash.front(); trash.pop(); return ret; 801 | } 802 | } pool[M], *pit = pool, *null = new P{0, 0, -1, -1}; 803 | P* N(LL v, P* ls = null, P* rs = null) { 804 | if (ls->d < rs->d) swap(ls, rs); 805 | return new P{ls, rs, v, rs->d + 1}; 806 | } 807 | P* merge(P* a, P* b) { 808 | if (a == null) return b; 809 | if (b == null) return a; 810 | if (a->v < b->v) 811 | return N(a->v, a->ls, merge(a->rs, b)); 812 | else 813 | return N(b->v, b->ls, merge(b->rs, a)); 814 | } 815 | 816 | LL pop(P*& o) { 817 | LL ret = o->v; 818 | o = merge(o->ls, o->rs); 819 | return ret; 820 | } 821 | } 822 | ``` 823 | 824 | ## Treap 825 | 826 | + 非旋 Treap 827 | + v 小根堆 828 | + 模板题 bzoj 3224 829 | + lower 第一个大于等于的是第几个 (0-based) 830 | + upper 第一个大于的是第几个 (0-based) 831 | + split 左侧分割出 rk 个元素 832 | + 树套树 略 833 | 834 | ```cpp 835 | namespace treap { 836 | const int M = maxn * 17; 837 | extern struct P* const null; 838 | struct P { 839 | P *ls, *rs; 840 | int v, sz; 841 | unsigned rd; 842 | P(int v): ls(null), rs(null), v(v), sz(1), rd(rnd()) {} 843 | P(): sz(0) {} 844 | 845 | P* up() { sz = ls->sz + rs->sz + 1; return this; } 846 | int lower(int v) { 847 | if (this == null) return 0; 848 | return this->v >= v ? ls->lower(v) : rs->lower(v) + ls->sz + 1; 849 | } 850 | int upper(int v) { 851 | if (this == null) return 0; 852 | return this->v > v ? ls->upper(v) : rs->upper(v) + ls->sz + 1; 853 | } 854 | } *const null = new P, pool[M], *pit = pool; 855 | 856 | P* merge(P* l, P* r) { 857 | if (l == null) return r; if (r == null) return l; 858 | if (l->rd < r->rd) { l->rs = merge(l->rs, r); return l->up(); } 859 | else { r->ls = merge(l, r->ls); return r->up(); } 860 | } 861 | 862 | void split(P* o, int rk, P*& l, P*& r) { 863 | if (o == null) { l = r = null; return; } 864 | if (o->ls->sz >= rk) { split(o->ls, rk, l, o->ls); r = o->up(); } 865 | else { split(o->rs, rk - o->ls->sz - 1, o->rs, r); l = o->up(); } 866 | } 867 | } 868 | ``` 869 | 870 | + 持久化 Treap 871 | 872 | ```cpp 873 | namespace treap { 874 | const int M = maxn * 17 * 12; 875 | extern struct P* const null, *pit; 876 | struct P { 877 | P *ls, *rs; 878 | int v, sz; 879 | LL sum; 880 | P(P* ls, P* rs, int v): ls(ls), rs(rs), v(v), sz(ls->sz + rs->sz + 1), 881 | sum(ls->sum + rs->sum + v) {} 882 | P() {} 883 | 884 | void* operator new(size_t _) { return pit++; } 885 | template 886 | int rk(int v, T&& cmp) { 887 | if (this == null) return 0; 888 | return cmp(this->v, v) ? ls->rk(v, cmp) : rs->rk(v, cmp) + ls->sz + 1; 889 | } 890 | int lower(int v) { return rk(v, greater_equal()); } 891 | int upper(int v) { return rk(v, greater()); } 892 | } pool[M], *pit = pool, *const null = new P; 893 | P* merge(P* l, P* r) { 894 | if (l == null) return r; if (r == null) return l; 895 | if (rnd() % (l->sz + r->sz) < l->sz) return new P{l->ls, merge(l->rs, r), l->v}; 896 | else return new P{merge(l, r->ls), r->rs, r->v}; 897 | } 898 | void split(P* o, int rk, P*& l, P*& r) { 899 | if (o == null) { l = r = null; return; } 900 | if (o->ls->sz >= rk) { split(o->ls, rk, l, r); r = new P{r, o->rs, o->v}; } 901 | else { split(o->rs, rk - o->ls->sz - 1, l, r); l = new P{o->ls, l, o->v}; } 902 | } 903 | } 904 | ``` 905 | 906 | + 带 pushdown 的持久化 Treap 907 | + 注意任何修改操作前一定要 FIX 908 | 909 | ```cpp 910 | int now; 911 | namespace Treap { 912 | const int M = 10000000; 913 | extern struct P* const null, *pit; 914 | struct P { 915 | P *ls, *rs; 916 | int sz, time; 917 | LL cnt, sc, pos, add; 918 | bool rev; 919 | 920 | P* up() { sz = ls->sz + rs->sz + 1; sc = ls->sc + rs->sc + cnt; return this; } // MOD 921 | P* check() { 922 | if (time == now) return this; 923 | P* t = new(pit++) P; *t = *this; t->time = now; return t; 924 | }; 925 | P* _do_rev() { rev ^= 1; add *= -1; pos *= -1; swap(ls, rs); return this; } // MOD 926 | P* _do_add(LL v) { add += v; pos += v; return this; } // MOD 927 | P* do_rev() { if (this == null) return this; return check()->_do_rev(); } // FIX & MOD 928 | P* do_add(LL v) { if (this == null) return this; return check()->_do_add(v); } // FIX & MOD 929 | P* _down() { // MOD 930 | if (rev) { ls = ls->do_rev(); rs = rs->do_rev(); rev = 0; } 931 | if (add) { ls = ls->do_add(add); rs = rs->do_add(add); add = 0; } 932 | return this; 933 | } 934 | P* down() { return check()->_down(); } // FIX & MOD 935 | void _split(LL p, P*& l, P*& r) { // MOD 936 | if (pos >= p) { ls->split(p, l, r); ls = r; r = up(); } 937 | else { rs->split(p, l, r); rs = l; l = up(); } 938 | } 939 | void split(LL p, P*& l, P*& r) { // FIX & MOD 940 | if (this == null) l = r = null; 941 | else down()->_split(p, l, r); 942 | } 943 | } pool[M], *pit = pool, *const null = new P; 944 | P* merge(P* a, P* b) { 945 | if (a == null) return b; if (b == null) return a; 946 | if (rand() % (a->sz + b->sz) < a->sz) { a = a->down(); a->rs = merge(a->rs, b); return a->up(); } 947 | else { b = b->down(); b->ls = merge(a, b->ls); return b->up(); } 948 | } 949 | } 950 | ``` 951 | 952 | ## Treap-序列 953 | 954 | + 区间 ADD,SUM 955 | 956 | ```cpp 957 | namespace treap { 958 | const int M = 8E5 + 100; 959 | extern struct P*const null; 960 | struct P { 961 | P *ls, *rs; 962 | int sz, val, add, sum; 963 | P(int v, P* ls = null, P* rs = null): ls(ls), rs(rs), sz(1), val(v), add(0), sum(v) {} 964 | P(): sz(0), val(0), add(0), sum(0) {} 965 | 966 | P* up() { 967 | assert(this != null); 968 | sz = ls->sz + rs->sz + 1; 969 | sum = ls->sum + rs->sum + val + add * sz; 970 | return this; 971 | } 972 | void upd(int v) { 973 | if (this == null) return; 974 | add += v; 975 | sum += sz * v; 976 | } 977 | P* down() { 978 | if (add) { 979 | ls->upd(add); rs->upd(add); 980 | val += add; 981 | add = 0; 982 | } 983 | return this; 984 | } 985 | 986 | P* select(int rk) { 987 | if (rk == ls->sz + 1) return this; 988 | return ls->sz >= rk ? ls->select(rk) : rs->select(rk - ls->sz - 1); 989 | } 990 | } pool[M], *pit = pool, *const null = new P, *rt = null; 991 | 992 | P* merge(P* a, P* b) { 993 | if (a == null) return b->up(); 994 | if (b == null) return a->up(); 995 | if (rand() % (a->sz + b->sz) < a->sz) { 996 | a->down()->rs = merge(a->rs, b); 997 | return a->up(); 998 | } else { 999 | b->down()->ls = merge(a, b->ls); 1000 | return b->up(); 1001 | } 1002 | } 1003 | 1004 | void split(P* o, int rk, P*& l, P*& r) { 1005 | if (o == null) { l = r = null; return; } 1006 | o->down(); 1007 | if (o->ls->sz >= rk) { 1008 | split(o->ls, rk, l, o->ls); 1009 | r = o->up(); 1010 | } else { 1011 | split(o->rs, rk - o->ls->sz - 1, o->rs, r); 1012 | l = o->up(); 1013 | } 1014 | } 1015 | 1016 | inline void insert(int k, int v) { 1017 | P *l, *r; 1018 | split(rt, k - 1, l, r); 1019 | rt = merge(merge(l, new (pit++) P(v)), r); 1020 | } 1021 | 1022 | inline void erase(int k) { 1023 | P *l, *r, *_, *t; 1024 | split(rt, k - 1, l, t); 1025 | split(t, 1, _, r); 1026 | rt = merge(l, r); 1027 | } 1028 | 1029 | P* build(int l, int r, int* a) { 1030 | if (l > r) return null; 1031 | if (l == r) return new(pit++) P(a[l]); 1032 | int m = (l + r) / 2; 1033 | return (new(pit++) P(a[m], build(l, m - 1, a), build(m + 1, r, a)))->up(); 1034 | } 1035 | }; 1036 | ``` 1037 | 1038 | + 区间 REVERSE,ADD,MIN 1039 | 1040 | ```cpp 1041 | namespace treap { 1042 | extern struct P*const null; 1043 | struct P { 1044 | P *ls, *rs; 1045 | int sz, v, add, m; 1046 | bool flip; 1047 | P(int v, P* ls = null, P* rs = null): ls(ls), rs(rs), sz(1), v(v), add(0), m(v), flip(0) {} 1048 | P(): sz(0), v(INF), m(INF) {} 1049 | 1050 | void upd(int v) { 1051 | if (this == null) return; 1052 | add += v; m += v; 1053 | } 1054 | void rev() { 1055 | if (this == null) return; 1056 | swap(ls, rs); 1057 | flip ^= 1; 1058 | } 1059 | P* up() { 1060 | assert(this != null); 1061 | sz = ls->sz + rs->sz + 1; 1062 | m = min(min(ls->m, rs->m), v) + add; 1063 | return this; 1064 | } 1065 | P* down() { 1066 | if (add) { 1067 | ls->upd(add); rs->upd(add); 1068 | v += add; 1069 | add = 0; 1070 | } 1071 | if (flip) { 1072 | ls->rev(); rs->rev(); 1073 | flip = 0; 1074 | } 1075 | return this; 1076 | } 1077 | 1078 | P* select(int k) { 1079 | if (ls->sz + 1 == k) return this; 1080 | if (ls->sz >= k) return ls->select(k); 1081 | return rs->select(k - ls->sz - 1); 1082 | } 1083 | 1084 | } pool[M], *const null = new P, *pit = pool, *rt = null; 1085 | 1086 | P* merge(P* a, P* b) { 1087 | if (a == null) return b; 1088 | if (b == null) return a; 1089 | if (rnd() % (a->sz + b->sz) < a->sz) { 1090 | a->down()->rs = merge(a->rs, b); 1091 | return a->up(); 1092 | } else { 1093 | b->down()->ls = merge(a, b->ls); 1094 | return b->up(); 1095 | } 1096 | } 1097 | 1098 | void split(P* o, int k, P*& l, P*& r) { 1099 | if (o == null) { l = r = null; return; } 1100 | o->down(); 1101 | if (o->ls->sz >= k) { 1102 | split(o->ls, k, l, o->ls); 1103 | r = o->up(); 1104 | } else { 1105 | split(o->rs, k - o->ls->sz - 1, o->rs, r); 1106 | l = o->up(); 1107 | } 1108 | } 1109 | 1110 | P* build(int l, int r, int* v) { 1111 | if (l > r) return null; 1112 | int m = (l + r) >> 1; 1113 | return (new (pit++) P(v[m], build(l, m - 1, v), build(m + 1, r, v)))->up(); 1114 | } 1115 | 1116 | void go(int x, int y, void f(P*&)) { 1117 | P *l, *m, *r; 1118 | split(rt, y, l, r); 1119 | split(l, x - 1, l, m); 1120 | f(m); 1121 | rt = merge(merge(l, m), r); 1122 | } 1123 | } 1124 | using namespace treap; 1125 | int a[maxn], n, x, y, Q, v, k, d; 1126 | char s[100]; 1127 | 1128 | int main() { 1129 | cin >> n; 1130 | FOR (i, 1, n + 1) scanf("%d", &a[i]); 1131 | rt = build(1, n, a); 1132 | cin >> Q; 1133 | while (Q--) { 1134 | scanf("%s", s); 1135 | if (s[0] == 'A') { 1136 | scanf("%d%d%d", &x, &y, &v); 1137 | go(x, y, [](P*& o){ o->upd(v); }); 1138 | } else if (s[0] == 'R' && s[3] == 'E') { 1139 | scanf("%d%d", &x, &y); 1140 | go(x, y, [](P*& o){ o->rev(); }); 1141 | } else if (s[0] == 'R' && s[3] == 'O') { 1142 | scanf("%d%d%d", &x, &y, &d); 1143 | d %= y - x + 1; 1144 | go(x, y, [](P*& o){ 1145 | P *l, *r; 1146 | split(o, o->sz - d, l, r); 1147 | o = merge(r, l); 1148 | }); 1149 | } else if (s[0] == 'I') { 1150 | scanf("%d%d", &k, &v); 1151 | go(k + 1, k, [](P*& o){ o = new (pit++) P(v); }); 1152 | } else if (s[0] == 'D') { 1153 | scanf("%d", &k); 1154 | go(k, k, [](P*& o){ o = null; }); 1155 | } else if (s[0] == 'M') { 1156 | scanf("%d%d", &x, &y); 1157 | go(x, y, [](P*& o) { 1158 | printf("%d\n", o->m); 1159 | }); 1160 | } 1161 | } 1162 | } 1163 | 1164 | ``` 1165 | 1166 | + 持久化 1167 | 1168 | ```cpp 1169 | namespace treap { 1170 | struct P; 1171 | extern P*const null; 1172 | P* N(P* ls, P* rs, LL v, bool fill); 1173 | struct P { 1174 | P *const ls, *const rs; 1175 | const int sz, v; 1176 | const LL sum; 1177 | bool fill; 1178 | int cnt; 1179 | 1180 | void split(int k, P*& l, P*& r) { 1181 | if (this == null) { l = r = null; return; } 1182 | if (ls->sz >= k) { 1183 | ls->split(k, l, r); 1184 | r = N(r, rs, v, fill); 1185 | } else { 1186 | rs->split(k - ls->sz - fill, l, r); 1187 | l = N(ls, l, v, fill); 1188 | } 1189 | } 1190 | 1191 | 1192 | } *const null = new P{0, 0, 0, 0, 0, 0, 1}; 1193 | 1194 | P* N(P* ls, P* rs, LL v, bool fill) { 1195 | ls->cnt++; rs->cnt++; 1196 | return new P{ls, rs, ls->sz + rs->sz + fill, v, ls->sum + rs->sum + v, fill, 1}; 1197 | } 1198 | 1199 | P* merge(P* a, P* b) { 1200 | if (a == null) return b; 1201 | if (b == null) return a; 1202 | if (rand() % (a->sz + b->sz) < a->sz) 1203 | return N(a->ls, merge(a->rs, b), a->v, a->fill); 1204 | else 1205 | return N(merge(a, b->ls), b->rs, b->v, b->fill); 1206 | } 1207 | 1208 | void go(P* o, int x, int y, P*& l, P*& m, P*& r) { 1209 | o->split(y, l, r); 1210 | l->split(x - 1, l, m); 1211 | } 1212 | } 1213 | ``` 1214 | 1215 | ## 可回滚并查集 1216 | 1217 | + 注意这个不是可持久化并查集 1218 | + 查找时不进行路径压缩 1219 | + 复杂度靠按秩合并解决 1220 | 1221 | ```cpp 1222 | namespace uf { 1223 | int fa[maxn], sz[maxn]; 1224 | int undo[maxn], top; 1225 | void init() { memset(fa, -1, sizeof fa); memset(sz, 0, sizeof sz); top = 0; } 1226 | int findset(int x) { while (fa[x] != -1) x = fa[x]; return x; } 1227 | bool join(int x, int y) { 1228 | x = findset(x); y = findset(y); 1229 | if (x == y) return false; 1230 | if (sz[x] > sz[y]) swap(x, y); 1231 | undo[top++] = x; 1232 | fa[x] = y; 1233 | sz[y] += sz[x] + 1; 1234 | return true; 1235 | } 1236 | inline int checkpoint() { return top; } 1237 | void rewind(int t) { 1238 | while (top > t) { 1239 | int x = undo[--top]; 1240 | sz[fa[x]] -= sz[x] + 1; 1241 | fa[x] = -1; 1242 | } 1243 | } 1244 | } 1245 | ``` 1246 | 1247 | ## 舞蹈链 1248 | 1249 | 1250 | + 注意 link 的 y 的范围是 [1, n] 1251 | + 注意在某些情况下替换掉 memset 1252 | 1253 | + 精确覆盖 1254 | 1255 | ```cpp 1256 | struct P { 1257 | P *L, *R, *U, *D; 1258 | int x, y; 1259 | }; 1260 | 1261 | const int INF = 1E9; 1262 | 1263 | struct DLX { 1264 | #define TR(i, D, s) for (P* i = s->D; i != s; i = i->D) 1265 | static const int M = 2E5; 1266 | P pool[M], *h[M], *r[M], *pit; 1267 | int sz[M]; 1268 | bool solved; 1269 | stack ans; 1270 | void init(int n) { 1271 | pit = pool; 1272 | ++n; 1273 | solved = false; 1274 | while (!ans.empty()) ans.pop(); 1275 | memset(r, 0, sizeof r); 1276 | memset(sz, 0, sizeof sz); 1277 | FOR (i, 0, n) 1278 | h[i] = new (pit++) P; 1279 | FOR (i, 0, n) { 1280 | h[i]->L = h[(i + n - 1) % n]; 1281 | h[i]->R = h[(i + 1) % n]; 1282 | h[i]->U = h[i]->D = h[i]; 1283 | h[i]->y = i; 1284 | } 1285 | } 1286 | 1287 | void link(int x, int y) { 1288 | sz[y]++; 1289 | auto p = new (pit++) P; 1290 | p->x = x; p->y = y; 1291 | p->U = h[y]->U; p->D = h[y]; 1292 | p->D->U = p->U->D = p; 1293 | if (!r[x]) r[x] = p->L = p->R = p; 1294 | else { 1295 | p->L = r[x]; p->R = r[x]->R; 1296 | p->L->R = p->R->L = p; 1297 | } 1298 | } 1299 | 1300 | void remove(P* p) { 1301 | p->L->R = p->R; p->R->L = p->L; 1302 | TR (i, D, p) 1303 | TR (j, R, i) { 1304 | j->D->U = j->U; j->U->D = j->D; 1305 | sz[j->y]--; 1306 | } 1307 | } 1308 | 1309 | void recall(P* p) { 1310 | p->L->R = p->R->L = p; 1311 | TR (i, U, p) 1312 | TR (j, L, i) { 1313 | j->D->U = j->U->D = j; 1314 | sz[j->y]++; 1315 | } 1316 | } 1317 | 1318 | bool dfs(int d) { 1319 | if (solved) return true; 1320 | if (h[0]->R == h[0]) return solved = true; 1321 | int m = INF; 1322 | P* c; 1323 | TR (i, R, h[0]) 1324 | if (sz[i->y] < m) { m = sz[i->y]; c = i; } 1325 | remove(c); 1326 | TR (i, D, c) { 1327 | ans.push(i->x); 1328 | TR (j, R, i) remove(h[j->y]); 1329 | if (dfs(d + 1)) return true; 1330 | TR (j, L, i) recall(h[j->y]); 1331 | ans.pop(); 1332 | } 1333 | recall(c); 1334 | return false; 1335 | } 1336 | } dlx; 1337 | ``` 1338 | 1339 | 1340 | + 可重复覆盖 1341 | 1342 | ```cpp 1343 | struct P { 1344 | P *L, *R, *U, *D; 1345 | int x, y; 1346 | }; 1347 | 1348 | const int INF = 1E9; 1349 | 1350 | struct DLX { 1351 | #define TR(i, D, s) for (P* i = s->D; i != s; i = i->D) 1352 | static const int M = 2E5; 1353 | P pool[M], *h[M], *r[M], *pit; 1354 | int sz[M], vis[M], ans, clk; 1355 | void init(int n) { 1356 | clk = 0; 1357 | ans = INF; 1358 | pit = pool; 1359 | ++n; 1360 | memset(r, 0, sizeof r); 1361 | memset(sz, 0, sizeof sz); 1362 | memset(vis, -1, sizeof vis); 1363 | FOR (i, 0, n) 1364 | h[i] = new (pit++) P; 1365 | FOR (i, 0, n) { 1366 | h[i]->L = h[(i + n - 1) % n]; 1367 | h[i]->R = h[(i + 1) % n]; 1368 | h[i]->U = h[i]->D = h[i]; 1369 | h[i]->y = i; 1370 | } 1371 | } 1372 | 1373 | void link(int x, int y) { 1374 | sz[y]++; 1375 | auto p = new (pit++) P; 1376 | p->x = x; p->y = y; 1377 | p->U = h[y]->U; p->D = h[y]; 1378 | p->D->U = p->U->D = p; 1379 | if (!r[x]) r[x] = p->L = p->R = p; 1380 | else { 1381 | p->L = r[x]; p->R = r[x]->R; 1382 | p->L->R = p->R->L = p; 1383 | } 1384 | } 1385 | 1386 | void remove(P* p) { 1387 | TR (i, D, p) { 1388 | i->L->R = i->R; 1389 | i->R->L = i->L; 1390 | } 1391 | } 1392 | 1393 | void recall(P* p) { 1394 | TR (i, U, p) 1395 | i->L->R = i->R->L = i; 1396 | } 1397 | 1398 | int eval() { 1399 | ++clk; 1400 | int ret = 0; 1401 | TR (i, R, h[0]) 1402 | if (vis[i->y] != clk) { 1403 | ++ret; 1404 | vis[i->y] = clk; 1405 | TR (j, D, i) 1406 | TR (k, R, j) 1407 | vis[k->y] = clk; 1408 | } 1409 | return ret; 1410 | } 1411 | 1412 | void dfs(int d) { 1413 | if (h[0]->R == h[0]) { ans = min(ans, d); return; } 1414 | if (eval() + d >= ans) return; 1415 | P* c; 1416 | int m = INF; 1417 | TR (i, R, h[0]) 1418 | if (sz[i->y] < m) { m = sz[i->y]; c = i; } 1419 | TR (i, D, c) { 1420 | remove(i); 1421 | TR (j, R, i) remove(j); 1422 | dfs(d + 1); 1423 | TR (j, L, i) recall(j); 1424 | recall(i); 1425 | } 1426 | } 1427 | } dlx; 1428 | ``` 1429 | 1430 | ## CDQ 分治 1431 | 1432 | ```cpp 1433 | const int maxn = 2E5 + 100; 1434 | struct P { 1435 | int x, y; 1436 | int* f; 1437 | bool d1, d2; 1438 | } a[maxn], b[maxn], c[maxn]; 1439 | int f[maxn]; 1440 | 1441 | void go2(int l, int r) { 1442 | if (l + 1 == r) return; 1443 | int m = (l + r) >> 1; 1444 | go2(l, m); go2(m, r); 1445 | FOR (i, l, m) b[i].d2 = 0; 1446 | FOR (i, m, r) b[i].d2 = 1; 1447 | merge(b + l, b + m, b + m, b + r, c + l, [](const P& a, const P& b)->bool { 1448 | if (a.y != b.y) return a.y < b.y; 1449 | return a.d2 > b.d2; 1450 | }); 1451 | int mx = -1; 1452 | FOR (i, l, r) { 1453 | if (c[i].d1 && c[i].d2) *c[i].f = max(*c[i].f, mx + 1); 1454 | if (!c[i].d1 && !c[i].d2) mx = max(mx, *c[i].f); 1455 | } 1456 | FOR (i, l, r) b[i] = c[i]; 1457 | } 1458 | 1459 | void go1(int l, int r) { // [l, r) 1460 | if (l + 1 == r) return; 1461 | int m = (l + r) >> 1; 1462 | go1(l, m); 1463 | FOR (i, l, m) a[i].d1 = 0; 1464 | FOR (i, m, r) a[i].d1 = 1; 1465 | copy(a + l, a + r, b + l); 1466 | sort(b + l, b + r, [](const P& a, const P& b)->bool { 1467 | if (a.x != b.x) return a.x < b.x; 1468 | return a.d1 > b.d1; 1469 | }); 1470 | go2(l, r); 1471 | go1(m, r); 1472 | } 1473 | ``` 1474 | 1475 | + k 维 LIS 1476 | 1477 | ```cpp 1478 | struct P { 1479 | int v[K]; 1480 | LL f; 1481 | bool d[K]; 1482 | } o[N << 10]; 1483 | P* a[K][N << 10]; 1484 | int k; 1485 | void go(int now, int l, int r) { 1486 | if (now == 0) { 1487 | if (l + 1 == r) return; 1488 | int m = (l + r) / 2; 1489 | go(now, l, m); 1490 | FOR (i, l, m) a[now][i]->d[now] = 0; 1491 | FOR (i, m, r) a[now][i]->d[now] = 1; 1492 | copy(a[now] + l, a[now] + r, a[now + 1] + l); 1493 | sort(a[now + 1] + l, a[now + 1] + r, [now](const P* a, const P* b){ 1494 | if (a->v[now] != b->v[now]) return a->v[now] < b->v[now]; 1495 | return a->d[now] > b->d[now]; 1496 | }); 1497 | go(now + 1, l, r); 1498 | go(now, m, r); 1499 | } else { 1500 | if (l + 1 == r) return; 1501 | int m = (l + r) / 2; 1502 | go(now, l, m); go(now, m, r); 1503 | FOR (i, l, m) a[now][i]->d[now] = 0; 1504 | FOR (i, m, r) a[now][i]->d[now] = 1; 1505 | merge(a[now] + l, a[now] + m, a[now] + m, a[now] + r, a[now + 1] + l, [now](const P* a, const P* b){ 1506 | if (a->v[now] != b->v[now]) return a->v[now] < b->v[now]; 1507 | return a->d[now] > b->d[now]; 1508 | }); 1509 | copy(a[now + 1] + l, a[now + 1] + r, a[now] + l); 1510 | if (now < k - 2) { 1511 | go(now + 1, l, r); 1512 | } else { 1513 | LL sum = 0; 1514 | FOR (i, l, r) { 1515 | dbg(a[now][i]->v[0], a[now][i]->v[1], a[now][i]->f, 1516 | a[now][i]->d[0], a[now][i]->d[1]); 1517 | int cnt = 0; 1518 | FOR (j, 0, now + 1) cnt += a[now][i]->d[j]; 1519 | if (cnt == 0) { 1520 | sum += a[now][i]->f; 1521 | } else if (cnt == now + 1) { 1522 | a[now][i]->f = (a[now][i]->f + sum) % MOD; 1523 | } 1524 | } 1525 | } 1526 | } 1527 | } 1528 | ``` 1529 | 1530 | ## 笛卡尔树 1531 | 1532 | ```cpp 1533 | void build(const vector& a) { 1534 | static P *stack[M], *x, *last; 1535 | int p = 0; 1536 | FOR (i, 0, a.size()) { 1537 | x = new P(i + 1, a[i]); 1538 | last = null; 1539 | while (p && stack[p - 1]->v > x->v) { 1540 | stack[p - 1]->maintain(); 1541 | last = stack[--p]; 1542 | } 1543 | if (p) stack[p - 1]->rs = x; 1544 | x->ls = last; 1545 | stack[p++] = x; 1546 | } 1547 | while (p) 1548 | stack[--p]->maintain(); 1549 | rt = stack[0]; 1550 | } 1551 | ``` 1552 | 1553 | ```cpp 1554 | void build() { 1555 | static int s[N], last; 1556 | int p = 0; 1557 | FOR (x, 1, n + 1) { 1558 | last = 0; 1559 | while (p && val[s[p - 1]] > val[x]) last = s[--p]; 1560 | if (p) G[s[p - 1]][1] = x; 1561 | if (last) G[x][0] = last; 1562 | s[p++] = x; 1563 | } 1564 | rt = s[0]; 1565 | } 1566 | ``` 1567 | 1568 | 1569 | 1570 | ## Trie 1571 | 1572 | * 二进制 Trie 1573 | 1574 | ```cpp 1575 | namespace trie { 1576 | const int M = 31; 1577 | int ch[N * M][2], sz; 1578 | void init() { memset(ch, 0, sizeof ch); sz = 2; } 1579 | void ins(LL x) { 1580 | int u = 1; 1581 | FORD (i, M, -1) { 1582 | bool b = x & (1LL << i); 1583 | if (!ch[u][b]) ch[u][b] = sz++; 1584 | u = ch[u][b]; 1585 | } 1586 | } 1587 | } 1588 | ``` 1589 | 1590 | + 持久化二进制 Trie 1591 | + `sz=1` 1592 | 1593 | ```cpp 1594 | struct P { int w, ls, rs; }; 1595 | P tr[M] = {{0, 0, 0}}; 1596 | int sz; 1597 | 1598 | int _new(int w, int ls, int rs) { tr[sz] = {w, ls, rs}; return sz++; } 1599 | int ins(int oo, int v, int d = 30) { 1600 | P& o = tr[oo]; 1601 | if (d == -1) return _new(o.w + 1, 0, 0); 1602 | bool u = v & (1 << d); 1603 | return _new(o.w + 1, u == 0 ? ins(o.ls, v, d - 1) : o.ls, u == 1 ? ins(o.rs, v, d - 1) : o.rs); 1604 | } 1605 | int query(int pp, int qq, int v, int d = 30) { 1606 | if (d == -1) return 0; 1607 | bool u = v & (1 << d); 1608 | P &p = tr[pp], &q = tr[qq]; 1609 | int lw = tr[q.ls].w - tr[p.ls].w; 1610 | int rw = tr[q.rs].w - tr[p.rs].w; 1611 | 1612 | int ret = 0; 1613 | if (u == 0) { 1614 | if (rw) { ret += 1 << d; ret += query(p.rs, q.rs, v, d - 1); } 1615 | else ret += query(p.ls, q.ls, v, d - 1); 1616 | } else { 1617 | if (lw) { ret += 1 << d; ret += query(p.ls, q.ls, v, d - 1); } 1618 | else ret += query(p.rs, q.rs, v, d - 1); 1619 | } 1620 | return ret; 1621 | } 1622 | ``` 1623 | 1624 | ## exSTL 1625 | 1626 | ### 优先队列 1627 | 1628 | + binary_heap_tag 1629 | + pairing_heap_tag 支持修改 1630 | + thin_heap_tag 如果修改只有 increase 则较快,不支持 join 1631 | 1632 | ```cpp 1633 | #include 1634 | using namespace __gnu_pbds; 1635 | 1636 | typedef __gnu_pbds::priority_queue, pairing_heap_tag> PQ; 1637 | __gnu_pbds::priority_queue::point_iterator it; 1638 | PQ pq, pq2; 1639 | 1640 | int main() { 1641 | auto it = pq.push(2); 1642 | pq.push(3); 1643 | assert(pq.top() == 3); 1644 | pq.modify(it, 4); 1645 | assert(pq.top() == 4); 1646 | pq2.push(5); 1647 | pq.join(pq2); 1648 | assert(pq.top() == 5); 1649 | } 1650 | ``` 1651 | 1652 | ### 平衡树 1653 | 1654 | + ov_tree_tag 1655 | + rb_tree_tag 1656 | + splay_tree_tag 1657 | 1658 | + mapped: null_type 或 null_mapped_type(旧版本) 为空 1659 | + Node_Update 为 tree_order_statistics_node_update 时才可以 find_by_order & order_of_key 1660 | + find_by_order 找 order + 1 小的元素 (其实都是从 0 开始计数),或者有 order 个元素比它小的 key 1661 | + order_of_key 有多少个比 r_key 小的元素 1662 | + join & split 1663 | 1664 | ```cpp 1665 | #include 1666 | using namespace __gnu_pbds; 1667 | using Tree = tree, rb_tree_tag, tree_order_statistics_node_update>; 1668 | Tree t; 1669 | ``` 1670 | 1671 | ### 持久化平衡树 1672 | 1673 | ```cpp 1674 | #include 1675 | using namespace __gnu_cxx; 1676 | rope s; 1677 | 1678 | int main() { 1679 | FOR (i, 0, 5) s.push_back(i); // 0 1 2 3 4 1680 | s.replace(1, 2, s); // 0 (0 1 2 3 4) 3 4 1681 | auto ss = s.substr(2, 2); // 1 2、 1682 | s.erase(2, 2); // 0 1 4 1683 | s.insert(2, s); // equal to s.replace(2, 0, s) 1684 | assert(s[2] == s.at(2)); // 2 1685 | } 1686 | ``` 1687 | 1688 | ### 哈希表 1689 | 1690 | ```cpp 1691 | #include 1692 | #include 1693 | using namespace __gnu_pbds; 1694 | 1695 | gp_hash_table mp; 1696 | cc_hash_table mp; 1697 | ``` 1698 | 1699 | ## Link-Cut Tree 1700 | 1701 | + 图中相邻的结点在伸展树中不一定是父子关系 1702 | + 遇事不决 `make_root` 1703 | + 跑左右儿子的时候不要忘记 `down` 1704 | 1705 | ```cpp 1706 | namespace lct { 1707 | extern struct P *const null; 1708 | const int M = N; 1709 | struct P { 1710 | P *fa, *ls, *rs; 1711 | int v, maxv; 1712 | bool rev; 1713 | 1714 | bool has_fa() { return fa->ls == this || fa->rs == this; } 1715 | bool d() { return fa->ls == this; } 1716 | P*& c(bool x) { return x ? ls : rs; } 1717 | void do_rev() { 1718 | if (this == null) return; 1719 | rev ^= 1; 1720 | swap(ls, rs); 1721 | } 1722 | P* up() { 1723 | maxv = max(v, max(ls->maxv, rs->maxv)); 1724 | return this; 1725 | } 1726 | void down() { 1727 | if (rev) { 1728 | rev = 0; 1729 | ls->do_rev(); rs->do_rev(); 1730 | } 1731 | } 1732 | void all_down() { if (has_fa()) fa->all_down(); down(); } 1733 | } *const null = new P{0, 0, 0, 0, 0, 0}, pool[M], *pit = pool; 1734 | 1735 | void rot(P* o) { 1736 | bool dd = o->d(); 1737 | P *f = o->fa, *t = o->c(!dd); 1738 | if (f->has_fa()) f->fa->c(f->d()) = o; o->fa = f->fa; 1739 | if (t != null) t->fa = f; f->c(dd) = t; 1740 | o->c(!dd) = f->up(); f->fa = o; 1741 | } 1742 | void splay(P* o) { 1743 | o->all_down(); 1744 | while (o->has_fa()) { 1745 | if (o->fa->has_fa()) 1746 | rot(o->d() ^ o->fa->d() ? o : o->fa); 1747 | rot(o); 1748 | } 1749 | o->up(); 1750 | } 1751 | void access(P* u, P* v = null) { 1752 | if (u == null) return; 1753 | splay(u); u->rs = v; 1754 | access(u->up()->fa, u); 1755 | } 1756 | void make_root(P* o) { 1757 | access(o); splay(o); o->do_rev(); 1758 | } 1759 | void split(P* o, P* u) { 1760 | make_root(o); access(u); splay(u); 1761 | } 1762 | void link(P* u, P* v) { 1763 | make_root(u); u->fa = v; 1764 | } 1765 | void cut(P* u, P* v) { 1766 | split(u, v); 1767 | u->fa = v->ls = null; v->up(); 1768 | } 1769 | bool adj(P* u, P* v) { 1770 | split(u, v); 1771 | return v->ls == u && u->ls == null && u->rs == null; 1772 | } 1773 | bool linked(P* u, P* v) { 1774 | split(u, v); 1775 | return u == v || u->fa != null; 1776 | } 1777 | P* findrt(P* o) { 1778 | access(o); splay(o); 1779 | while (o->ls != null) o = o->ls; 1780 | return o; 1781 | } 1782 | P* findfa(P* rt, P* u) { 1783 | split(rt, u); 1784 | u = u->ls; 1785 | while (u->rs != null) { 1786 | u = u->rs; 1787 | u->down(); 1788 | } 1789 | return u; 1790 | } 1791 | } 1792 | ``` 1793 | 1794 | + 维护子树大小 1795 | 1796 | ```cpp 1797 | P* up() { 1798 | sz = ls->sz + rs->sz + _sz + 1; 1799 | return this; 1800 | } 1801 | void access(P* u, P* v = null) { 1802 | if (u == null) return; 1803 | splay(u); 1804 | u->_sz += u->rs->sz - v->sz; 1805 | u->rs = v; 1806 | access(u->up()->fa, u); 1807 | } 1808 | void link(P* u, P* v) { 1809 | split(u, v); 1810 | u->fa = v; v->_sz += u->sz; 1811 | v->up(); 1812 | } 1813 | ``` 1814 | 1815 | 1816 | 1817 | ## 莫队 1818 | 1819 | + [l, r) 1820 | 1821 | ```cpp 1822 | while (l > q.l) mv(--l, 1); 1823 | while (r < q.r) mv(r++, 1); 1824 | while (l < q.l) mv(l++, -1); 1825 | while (r > q.r) mv(--r, -1); 1826 | ``` 1827 | 1828 | + 树上莫队 1829 | + 注意初始状态 u = v = 1, flip(1) 1830 | 1831 | ```cpp 1832 | struct Q { 1833 | int u, v, idx; 1834 | bool operator < (const Q& b) const { 1835 | const Q& a = *this; 1836 | return blk[a.u] < blk[b.u] || (blk[a.u] == blk[b.u] && in[a.v] < in[b.v]); 1837 | } 1838 | }; 1839 | 1840 | void dfs(int u = 1, int d = 0) { 1841 | static int S[maxn], sz = 0, blk_cnt = 0, clk = 0; 1842 | in[u] = clk++; 1843 | dep[u] = d; 1844 | int btm = sz; 1845 | for (int v: G[u]) { 1846 | if (v == fa[u]) continue; 1847 | fa[v] = u; 1848 | dfs(v, d + 1); 1849 | if (sz - btm >= B) { 1850 | while (sz > btm) blk[S[--sz]] = blk_cnt; 1851 | ++blk_cnt; 1852 | } 1853 | } 1854 | S[sz++] = u; 1855 | if (u == 1) while (sz) blk[S[--sz]] = blk_cnt - 1; 1856 | } 1857 | 1858 | void flip(int k) { 1859 | dbg(k); 1860 | if (vis[k]) { 1861 | // ... 1862 | } else { 1863 | // ... 1864 | } 1865 | vis[k] ^= 1; 1866 | } 1867 | 1868 | void go(int& k) { 1869 | if (bug == -1) { 1870 | if (vis[k] && !vis[fa[k]]) bug = k; 1871 | if (!vis[k] && vis[fa[k]]) bug = fa[k]; 1872 | } 1873 | flip(k); 1874 | k = fa[k]; 1875 | } 1876 | 1877 | void mv(int a, int b) { 1878 | bug = -1; 1879 | if (vis[b]) bug = b; 1880 | if (dep[a] < dep[b]) swap(a, b); 1881 | while (dep[a] > dep[b]) go(a); 1882 | while (a != b) { 1883 | go(a); go(b); 1884 | } 1885 | go(a); go(bug); 1886 | } 1887 | 1888 | for (Q& q: query) { 1889 | mv(u, q.u); u = q.u; 1890 | mv(v, q.v); v = q.v; 1891 | ans[q.idx] = Ans; 1892 | } 1893 | ``` -------------------------------------------------------------------------------- /2-数学.md: -------------------------------------------------------------------------------- 1 | # 数学 2 | 3 | ## 矩阵运算 4 | 5 | ```cpp 6 | struct Mat { 7 | static const LL M = 2; 8 | LL v[M][M]; 9 | Mat() { memset(v, 0, sizeof v); } 10 | void eye() { FOR (i, 0, M) v[i][i] = 1; } 11 | LL* operator [] (LL x) { return v[x]; } 12 | const LL* operator [] (LL x) const { return v[x]; } 13 | Mat operator * (const Mat& B) { 14 | const Mat& A = *this; 15 | Mat ret; 16 | FOR (k, 0, M) 17 | FOR (i, 0, M) if (A[i][k]) 18 | FOR (j, 0, M) 19 | ret[i][j] = (ret[i][j] + A[i][k] * B[k][j]) % MOD; 20 | return ret; 21 | } 22 | Mat pow(LL n) const { 23 | Mat A = *this, ret; ret.eye(); 24 | for (; n; n >>= 1, A = A * A) 25 | if (n & 1) ret = ret * A; 26 | return ret; 27 | } 28 | Mat operator + (const Mat& B) { 29 | const Mat& A = *this; 30 | Mat ret; 31 | FOR (i, 0, M) 32 | FOR (j, 0, M) 33 | ret[i][j] = (A[i][j] + B[i][j]) % MOD; 34 | return ret; 35 | } 36 | void prt() const { 37 | FOR (i, 0, M) 38 | FOR (j, 0, M) 39 | printf("%lld%c", (*this)[i][j], j == M - 1 ? '\n' : ' '); 40 | } 41 | }; 42 | ``` 43 | 44 | ## 筛 45 | 46 | * 线性筛 47 | 48 | ```cpp 49 | const LL p_max = 1E6 + 100; 50 | LL pr[p_max], p_sz; 51 | void get_prime() { 52 | static bool vis[p_max]; 53 | FOR (i, 2, p_max) { 54 | if (!vis[i]) pr[p_sz++] = i; 55 | FOR (j, 0, p_sz) { 56 | if (pr[j] * i >= p_max) break; 57 | vis[pr[j] * i] = 1; 58 | if (i % pr[j] == 0) break; 59 | } 60 | } 61 | } 62 | ``` 63 | 64 | * 线性筛+欧拉函数 65 | 66 | ```cpp 67 | const LL p_max = 1E5 + 100; 68 | LL phi[p_max]; 69 | void get_phi() { 70 | phi[1] = 1; 71 | static bool vis[p_max]; 72 | static LL prime[p_max], p_sz, d; 73 | FOR (i, 2, p_max) { 74 | if (!vis[i]) { 75 | prime[p_sz++] = i; 76 | phi[i] = i - 1; 77 | } 78 | for (LL j = 0; j < p_sz && (d = i * prime[j]) < p_max; ++j) { 79 | vis[d] = 1; 80 | if (i % prime[j] == 0) { 81 | phi[d] = phi[i] * prime[j]; 82 | break; 83 | } 84 | else phi[d] = phi[i] * (prime[j] - 1); 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | 91 | * 线性筛+莫比乌斯函数 92 | 93 | ```cpp 94 | const LL p_max = 1E5 + 100; 95 | LL mu[p_max]; 96 | void get_mu() { 97 | mu[1] = 1; 98 | static bool vis[p_max]; 99 | static LL prime[p_max], p_sz, d; 100 | FOR (i, 2, p_max) { 101 | if (!vis[i]) { 102 | prime[p_sz++] = i; 103 | mu[i] = -1; 104 | } 105 | for (LL j = 0; j < p_sz && (d = i * prime[j]) < p_max; ++j) { 106 | vis[d] = 1; 107 | if (i % prime[j] == 0) { 108 | mu[d] = 0; 109 | break; 110 | } 111 | else mu[d] = -mu[i]; 112 | } 113 | } 114 | } 115 | ``` 116 | 117 | ## 亚线性筛 118 | 119 | ### min_25 120 | 121 | ```cpp 122 | namespace min25 { 123 | const int M = 1E6 + 100; 124 | LL B, N; 125 | 126 | // g(x) 127 | inline LL pg(LL x) { return 1; } 128 | inline LL ph(LL x) { return x % MOD; } 129 | // Sum[g(i),{x,2,x}] 130 | inline LL psg(LL x) { return x % MOD - 1; } 131 | inline LL psh(LL x) { 132 | static LL inv2 = (MOD + 1) / 2; 133 | x %= MOD; 134 | return x * (x + 1) % MOD * inv2 % MOD - 1; 135 | } 136 | // f(pp=p^k) 137 | inline LL fpk(LL p, LL e, LL pp) { return (pp - pp / p) % MOD; } 138 | // f(p) = fgh(g(p), h(p)) 139 | inline LL fgh(LL g, LL h) { return h - g; } 140 | 141 | LL pr[M], pc, sg[M], sh[M]; 142 | void get_prime(LL n) { 143 | static bool vis[M]; pc = 0; 144 | FOR (i, 2, n + 1) { 145 | if (!vis[i]) { 146 | pr[pc++] = i; 147 | sg[pc] = (sg[pc - 1] + pg(i)) % MOD; 148 | sh[pc] = (sh[pc - 1] + ph(i)) % MOD; 149 | } 150 | FOR (j, 0, pc) { 151 | if (pr[j] * i > n) break; 152 | vis[pr[j] * i] = 1; 153 | if (i % pr[j] == 0) break; 154 | } 155 | } 156 | } 157 | 158 | LL w[M]; 159 | LL id1[M], id2[M], h[M], g[M]; 160 | inline LL id(LL x) { return x <= B ? id1[x] : id2[N / x]; } 161 | 162 | LL go(LL x, LL k) { 163 | if (x <= 1 || (k >= 0 && pr[k] > x)) return 0; 164 | LL t = id(x); 165 | LL ans = fgh((g[t] - sg[k + 1]), (h[t] - sh[k + 1])); 166 | FOR (i, k + 1, pc) { 167 | LL p = pr[i]; 168 | if (p * p > x) break; 169 | ans -= fgh(pg(p), ph(p)); 170 | for (LL pp = p, e = 1; pp <= x; ++e, pp = pp * p) 171 | ans += fpk(p, e, pp) * (1 + go(x / pp, i)) % MOD; 172 | } 173 | return ans % MOD; 174 | } 175 | 176 | LL solve(LL _N) { 177 | N = _N; 178 | B = sqrt(N + 0.5); 179 | get_prime(B); 180 | int sz = 0; 181 | for (LL l = 1, v, r; l <= N; l = r + 1) { 182 | v = N / l; r = N / v; 183 | w[sz] = v; g[sz] = psg(v); h[sz] = psh(v); 184 | if (v <= B) id1[v] = sz; else id2[r] = sz; 185 | sz++; 186 | } 187 | FOR (k, 0, pc) { 188 | LL p = pr[k]; 189 | FOR (i, 0, sz) { 190 | LL v = w[i]; if (p * p > v) break; 191 | LL t = id(v / p); 192 | g[i] = (g[i] - (g[t] - sg[k]) * pg(p)) % MOD; 193 | h[i] = (h[i] - (h[t] - sh[k]) * ph(p)) % MOD; 194 | } 195 | } 196 | return (go(N, -1) % MOD + MOD + 1) % MOD; 197 | } 198 | pair sump(LL l, LL r) { 199 | return {h[id(r)] - h[id(l - 1)], 200 | g[id(r)] - g[id(l - 1)]}; 201 | } 202 | } 203 | ``` 204 | 205 | ### 杜教筛 206 | 207 | 求 $S(n)=\sum_{i=1}^n f(i)$,其中 $f$ 是一个积性函数。 208 | 209 | 构造一个积性函数 $g$,那么由 $(f*g)(n)=\sum_{d|n}f(d)g(\frac{n}{d})$,得到 $f(n)=(f*g)(n)-\sum_{d|n,d= M) break; 236 | vis[d] = 1; 237 | if (i % pr[j] == 0) { 238 | f[d] = 0; 239 | break; 240 | } else f[d] = -f[i]; 241 | } 242 | } 243 | FOR (i, 2, M) f[i] += f[i - 1]; 244 | } 245 | inline LL s_fg(LL n) { return 1; } 246 | inline LL s_g(LL n) { return n; } 247 | 248 | LL N, rd[M]; 249 | bool vis[M]; 250 | LL go(LL n) { 251 | if (n < M) return f[n]; 252 | LL id = N / n; 253 | if (vis[id]) return rd[id]; 254 | vis[id] = true; 255 | LL& ret = rd[id] = s_fg(n); 256 | for (LL l = 2, v, r; l <= n; l = r + 1) { 257 | v = n / l; r = n / v; 258 | ret -= (s_g(r) - s_g(l - 1)) * go(v); 259 | } 260 | return ret; 261 | } 262 | LL solve(LL n) { 263 | N = n; 264 | memset(vis, 0, sizeof vis); 265 | return go(n); 266 | } 267 | } 268 | ``` 269 | 270 | 271 | 272 | ## 素数测试 273 | 274 | + 前置: 快速乘、快速幂 275 | + int 范围内只需检查 2, 7, 61 276 | + long long 范围 2, 325, 9375, 28178, 450775, 9780504, 1795265022 277 | + 3E15内 2, 2570940, 880937, 610386380, 4130785767 278 | + 4E13内 2, 2570940, 211991001, 3749873356 279 | + http://miller-rabin.appspot.com/ 280 | 281 | ```cpp 282 | bool checkQ(LL a, LL n) { 283 | if (n == 2) return 1; 284 | if (n == 1 || !(n & 1)) return 0; 285 | LL d = n - 1; 286 | while (!(d & 1)) d >>= 1; 287 | LL t = bin(a, d, n); // 不一定需要快速乘 288 | while (d != n - 1 && t != 1 && t != n - 1) { 289 | t = mul(t, t, n); 290 | d <<= 1; 291 | } 292 | return t == n - 1 || d & 1; 293 | } 294 | 295 | bool primeQ(LL n) { 296 | static vector t = {2, 325, 9375, 28178, 450775, 9780504, 1795265022}; 297 | if (n <= 1) return false; 298 | for (LL k: t) if (!checkQ(k, n)) return false; 299 | return true; 300 | } 301 | ``` 302 | 303 | ## Pollard-Rho 304 | 305 | ```cpp 306 | mt19937 mt(time(0)); 307 | LL pollard_rho(LL n, LL c) { 308 | LL x = uniform_int_distribution(1, n - 1)(mt), y = x; 309 | auto f = [&](LL v) { LL t = mul(v, v, n) + c; return t < n ? t : t - n; }; 310 | while (1) { 311 | x = f(x); y = f(f(y)); 312 | if (x == y) return n; 313 | LL d = gcd(abs(x - y), n); 314 | if (d != 1) return d; 315 | } 316 | } 317 | 318 | LL fac[100], fcnt; 319 | void get_fac(LL n, LL cc = 19260817) { 320 | if (n == 4) { fac[fcnt++] = 2; fac[fcnt++] = 2; return; } 321 | if (primeQ(n)) { fac[fcnt++] = n; return; } 322 | LL p = n; 323 | while (p == n) p = pollard_rho(n, --cc); 324 | get_fac(p); get_fac(n / p); 325 | } 326 | 327 | void go_fac(LL n) { fcnt = 0; if (n > 1) get_fac(n); } 328 | ``` 329 | 330 | ## BM 线性递推 331 | 332 | ```cpp 333 | namespace BerlekampMassey { 334 | using V = vector; 335 | inline void up(LL& a, LL b) { (a += b) %= MOD; } 336 | V mul(const V&a, const V& b, const V& m, int k) { 337 | V r; r.resize(2 * k - 1); 338 | FOR (i, 0, k) FOR (j, 0, k) up(r[i + j], a[i] * b[j]); 339 | FORD (i, k - 2, -1) { 340 | FOR (j, 0, k) up(r[i + j], r[i + k] * m[j]); 341 | r.pop_back(); 342 | } 343 | return r; 344 | } 345 | 346 | V pow(LL n, const V& m) { 347 | int k = (int) m.size() - 1; assert (m[k] == -1 || m[k] == MOD - 1); 348 | V r(k), x(k); r[0] = x[1] = 1; 349 | for (; n; n >>= 1, x = mul(x, x, m, k)) 350 | if (n & 1) r = mul(x, r, m, k); 351 | return r; 352 | } 353 | 354 | LL go(const V& a, const V& x, LL n) { 355 | // a: (-1, a1, a2, ..., ak).reverse 356 | // x: x1, x2, ..., xk 357 | // x[n] = sum[a[i]*x[n-i],{i,1,k}] 358 | int k = (int) a.size() - 1; 359 | if (n <= k) return x[n - 1]; 360 | if (a.size() == 2) return x[0] * bin(a[0], n - 1, MOD) % MOD; 361 | V r = pow(n - 1, a); 362 | LL ans = 0; 363 | FOR (i, 0, k) up(ans, r[i] * x[i]); 364 | return (ans + MOD) % MOD; 365 | } 366 | 367 | V BM(const V& x) { 368 | V C{-1}, B{-1}; 369 | LL L = 0, m = 1, b = 1; 370 | FOR (n, 0, x.size()) { 371 | LL d = 0; 372 | FOR (i, 0, L + 1) up(d, C[i] * x[n - i]); 373 | if (d == 0) { ++m; continue; } 374 | V T = C; 375 | LL c = MOD - d * get_inv(b, MOD) % MOD; 376 | FOR (_, C.size(), B.size() + m) C.push_back(0); 377 | FOR (i, 0, B.size()) up(C[i + m], c * B[i]); 378 | if (2 * L > n) { ++m; continue; } 379 | L = n + 1 - L; B.swap(T); b = d; m = 1; 380 | } 381 | reverse(C.begin(), C.end()); 382 | return C; 383 | } 384 | } 385 | ``` 386 | 387 | ## 扩展欧几里得 388 | 389 | * 求 $ax+by=gcd(a,b)$ 的一组解 390 | * 如果 $a$ 和 $b$ 互素,那么 $x$ 是 $a$ 在模 $b$ 下的逆元 391 | * 注意 $x$ 和 $y$ 可能是负数 392 | 393 | ```cpp 394 | LL ex_gcd(LL a, LL b, LL &x, LL &y) { 395 | if (b == 0) { x = 1; y = 0; return a; } 396 | LL ret = ex_gcd(b, a % b, y, x); 397 | y -= a / b * x; 398 | return ret; 399 | } 400 | ``` 401 | 402 | + 卡常欧几里得 403 | 404 | ```cpp 405 | inline int ctz(LL x) { return __builtin_ctzll(x); } 406 | LL gcd(LL a, LL b) { 407 | if (!a) return b; if (!b) return a; 408 | int t = ctz(a | b); 409 | a >>= ctz(a); 410 | do { 411 | b >>= ctz(b); 412 | if (a > b) swap(a, b); 413 | b -= a; 414 | } while (b); 415 | return a << t; 416 | } 417 | ``` 418 | 419 | ## 类欧几里得 420 | 421 | * $m = \lfloor \frac{an+b}{c} \rfloor$. 422 | * $f(a,b,c,n)=\sum_{i=0}^n\lfloor\frac{ai+b}{c}\rfloor$: 当 $a \ge c$ or $b \ge c$ 时,$f(a,b,c,n)=(\frac{a}{c})n(n+1)/2+(\frac{b}{c})(n+1)+f(a \bmod c,b \bmod c,c,n)$;否则 $f(a,b,c,n)=nm-f(c,c-b-1,a,m-1)$。 423 | * $g(a,b,c,n)=\sum_{i=0}^n i \lfloor\frac{ai+b}{c}\rfloor$: 当 $a \ge c$ or $b \ge c$ 时,$g(a,b,c,n)=(\frac{a}{c})n(n+1)(2n+1)/6+(\frac{b}{c})n(n+1)/2+g(a \bmod c,b \bmod c,c,n)$;否则 $g(a,b,c,n)=\frac{1}{2} (n(n+1)m-f(c,c-b-1,a,m-1)-h(c,c-b-1,a,m-1))$。 424 | * $h(a,b,c,n)=\sum_{i=0}^n\lfloor \frac{ai+b}{c} \rfloor^2$: 当 $a \ge c$ or $b \ge c$ 时,$h(a,b,c,n)=(\frac{a}{c})^2 n(n+1)(2n+1)/6 +(\frac{b}{c})^2 (n+1)+(\frac{a}{c})(\frac{b}{c})n(n+1)+h(a \bmod c, b \bmod c,c,n)+2(\frac{a}{c})g(a \bmod c,b \bmod c,c,n)+2(\frac{b}{c})f(a \bmod c,b \bmod c,c,n)$;否则 $h(a,b,c,n)=nm(m+1)-2g(c,c-b-1,a,m-1)-2f(c,c-b-1,a,m-1)-f(a,b,c,n)$。 425 | 426 | 427 | ## 逆元 428 | 429 | * 如果 $p$ 不是素数,使用拓展欧几里得 430 | 431 | * 前置模板:快速幂 / 扩展欧几里得 432 | 433 | ```cpp 434 | inline LL get_inv(LL x, LL p) { return bin(x, p - 2, p); } 435 | LL get_inv(LL a, LL M) { 436 | static LL x, y; 437 | assert(exgcd(a, M, x, y) == 1); 438 | return (x % M + M) % M; 439 | } 440 | ``` 441 | 442 | * 预处理 1~n 的逆元 443 | 444 | ```cpp 445 | LL inv[N]; 446 | void inv_init(LL n, LL p) { 447 | inv[1] = 1; 448 | FOR (i, 2, n) 449 | inv[i] = (p - p / i) * inv[p % i] % p; 450 | } 451 | ``` 452 | 453 | * 预处理阶乘及其逆元 454 | 455 | ```cpp 456 | LL invf[M], fac[M] = {1}; 457 | void fac_inv_init(LL n, LL p) { 458 | FOR (i, 1, n) 459 | fac[i] = i * fac[i - 1] % p; 460 | invf[n - 1] = bin(fac[n - 1], p - 2, p); 461 | FORD (i, n - 2, -1) 462 | invf[i] = invf[i + 1] * (i + 1) % p; 463 | } 464 | ``` 465 | 466 | ## 组合数 467 | 468 | + 如果数较小,模较大时使用逆元 469 | + 前置模板:逆元-预处理阶乘及其逆元 470 | 471 | ```cpp 472 | inline LL C(LL n, LL m) { // n >= m >= 0 473 | return n < m || m < 0 ? 0 : fac[n] * invf[m] % MOD * invf[n - m] % MOD; 474 | } 475 | ``` 476 | 477 | + 如果模数较小,数字较大,使用 Lucas 定理 478 | + 前置模板可选1:求组合数 (如果使用阶乘逆元,需`fac_inv_init(MOD, MOD);`) 479 | + 前置模板可选2:模数不固定下使用,无法单独使用。 480 | 481 | ```cpp 482 | LL C(LL n, LL m) { // m >= n >= 0 483 | if (m - n < n) n = m - n; 484 | if (n < 0) return 0; 485 | LL ret = 1; 486 | FOR (i, 1, n + 1) 487 | ret = ret * (m - n + i) % MOD * bin(i, MOD - 2, MOD) % MOD; 488 | return ret; 489 | } 490 | ``` 491 | 492 | ```cpp 493 | LL Lucas(LL n, LL m) { // m >= n >= 0 494 | return m ? C(n % MOD, m % MOD) * Lucas(n / MOD, m / MOD) % MOD : 1; 495 | } 496 | ``` 497 | 498 | * 组合数预处理 499 | 500 | ```cpp 501 | LL C[M][M]; 502 | void init_C(int n) { 503 | FOR (i, 0, n) { 504 | C[i][0] = C[i][i] = 1; 505 | FOR (j, 1, i) 506 | C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD; 507 | } 508 | } 509 | ``` 510 | 511 | ## 斯特灵数 512 | 513 | ### 第一类斯特灵数 514 | 515 | + 绝对值是 $n$ 个元素划分为 $k$ 个环排列的方案数。 516 | + $s(n,k)=s(n-1,k-1)+(n-1)s(n-1,k)$ 517 | 518 | ### 第二类斯特灵数 519 | 520 | + $n$ 个元素划分为 $k$ 个等价类的方案数 521 | + $S(n, k)=S(n-1,k-1)+kS(n-1, k)$ 522 | 523 | ```cpp 524 | S[0][0] = 1; 525 | FOR (i, 1, N) 526 | FOR (j, 1, i + 1) S[i][j] = (S[i - 1][j - 1] + j * S[i - 1][j]) % MOD; 527 | ``` 528 | 529 | ## FFT & NTT & FWT 530 | 531 | ### NTT 532 | 533 | ```cpp 534 | LL wn[N << 2], rev[N << 2]; 535 | int NTT_init(int n_) { 536 | int step = 0; int n = 1; 537 | for ( ; n < n_; n <<= 1) ++step; 538 | FOR (i, 1, n) 539 | rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (step - 1)); 540 | int g = bin(G, (MOD - 1) / n, MOD); 541 | wn[0] = 1; 542 | for (int i = 1; i <= n; ++i) 543 | wn[i] = wn[i - 1] * g % MOD; 544 | return n; 545 | } 546 | 547 | void NTT(LL a[], int n, int f) { 548 | FOR (i, 0, n) if (i < rev[i]) 549 | std::swap(a[i], a[rev[i]]); 550 | for (int k = 1; k < n; k <<= 1) { 551 | for (int i = 0; i < n; i += (k << 1)) { 552 | int t = n / (k << 1); 553 | FOR (j, 0, k) { 554 | LL w = f == 1 ? wn[t * j] : wn[n - t * j]; 555 | LL x = a[i + j]; 556 | LL y = a[i + j + k] * w % MOD; 557 | a[i + j] = (x + y) % MOD; 558 | a[i + j + k] = (x - y + MOD) % MOD; 559 | } 560 | } 561 | } 562 | if (f == -1) { 563 | LL ninv = get_inv(n, MOD); 564 | FOR (i, 0, n) 565 | a[i] = a[i] * ninv % MOD; 566 | } 567 | } 568 | ``` 569 | 570 | ### FFT 571 | 572 | + n 需补成 2 的幂 (n 必须超过 a 和 b 的最高指数之和) 573 | 574 | ```cpp 575 | typedef double LD; 576 | const LD PI = acos(-1); 577 | struct C { 578 | LD r, i; 579 | C(LD r = 0, LD i = 0): r(r), i(i) {} 580 | }; 581 | C operator + (const C& a, const C& b) { 582 | return C(a.r + b.r, a.i + b.i); 583 | } 584 | C operator - (const C& a, const C& b) { 585 | return C(a.r - b.r, a.i - b.i); 586 | } 587 | C operator * (const C& a, const C& b) { 588 | return C(a.r * b.r - a.i * b.i, a.r * b.i + a.i * b.r); 589 | } 590 | 591 | void FFT(C x[], int n, int p) { 592 | for (int i = 0, t = 0; i < n; ++i) { 593 | if (i > t) swap(x[i], x[t]); 594 | for (int j = n >> 1; (t ^= j) < j; j >>= 1); 595 | } 596 | for (int h = 2; h <= n; h <<= 1) { 597 | C wn(cos(p * 2 * PI / h), sin(p * 2 * PI / h)); 598 | for (int i = 0; i < n; i += h) { 599 | C w(1, 0), u; 600 | for (int j = i, k = h >> 1; j < i + k; ++j) { 601 | u = x[j + k] * w; 602 | x[j + k] = x[j] - u; 603 | x[j] = x[j] + u; 604 | w = w * wn; 605 | } 606 | } 607 | } 608 | if (p == -1) 609 | FOR (i, 0, n) 610 | x[i].r /= n; 611 | } 612 | 613 | void conv(C a[], C b[], int n) { 614 | FFT(a, n, 1); 615 | FFT(b, n, 1); 616 | FOR (i, 0, n) 617 | a[i] = a[i] * b[i]; 618 | FFT(a, n, -1); 619 | } 620 | ``` 621 | 622 | ### FWT 623 | 624 | + $C_k=\sum_{i \oplus j=k} A_i B_j$ 625 | + FWT 完后需要先模一遍 626 | 627 | ```cpp 628 | template 629 | void fwt(LL a[], int n, T f) { 630 | for (int d = 1; d < n; d *= 2) 631 | for (int i = 0, t = d * 2; i < n; i += t) 632 | FOR (j, 0, d) 633 | f(a[i + j], a[i + j + d]); 634 | } 635 | 636 | void AND(LL& a, LL& b) { a += b; } 637 | void OR(LL& a, LL& b) { b += a; } 638 | void XOR (LL& a, LL& b) { 639 | LL x = a, y = b; 640 | a = (x + y) % MOD; 641 | b = (x - y + MOD) % MOD; 642 | } 643 | void rAND(LL& a, LL& b) { a -= b; } 644 | void rOR(LL& a, LL& b) { b -= a; } 645 | void rXOR(LL& a, LL& b) { 646 | static LL INV2 = (MOD + 1) / 2; 647 | LL x = a, y = b; 648 | a = (x + y) * INV2 % MOD; 649 | b = (x - y + MOD) * INV2 % MOD; 650 | } 651 | ``` 652 | 653 | + FWT 子集卷积 654 | 655 | ```text 656 | a[popcount(x)][x] = A[x] 657 | b[popcount(x)][x] = B[x] 658 | fwt(a[i]) fwt(b[i]) 659 | c[i + j][x] += a[i][x] * b[j][x] 660 | rfwt(c[i]) 661 | ans[x] = c[popcount(x)][x] 662 | ``` 663 | 664 | ## simpson 自适应积分 665 | 666 | ```cpp 667 | LD simpson(LD l, LD r) { 668 | LD c = (l + r) / 2; 669 | return (f(l) + 4 * f(c) + f(r)) * (r - l) / 6; 670 | } 671 | 672 | LD asr(LD l, LD r, LD eps, LD S) { 673 | LD m = (l + r) / 2; 674 | LD L = simpson(l, m), R = simpson(m, r); 675 | if (fabs(L + R - S) < 15 * eps) return L + R + (L + R - S) / 15; 676 | return asr(l, m, eps / 2, L) + asr(m, r, eps / 2, R); 677 | } 678 | 679 | LD asr(LD l, LD r, LD eps) { return asr(l, r, eps, simpson(l, r)); } 680 | ``` 681 | 682 | + FWT 683 | 684 | ```cpp 685 | template 686 | void fwt(LL a[], int n, T f) { 687 | for (int d = 1; d < n; d *= 2) 688 | for (int i = 0, t = d * 2; i < n; i += t) 689 | FOR (j, 0, d) 690 | f(a[i + j], a[i + j + d]); 691 | } 692 | 693 | auto f = [](LL& a, LL& b) { // xor 694 | LL x = a, y = b; 695 | a = (x + y) % MOD; 696 | b = (x - y + MOD) % MOD; 697 | }; 698 | ``` 699 | 700 | ## 快速乘 701 | 702 | ```cpp 703 | LL mul(LL a, LL b, LL m) { 704 | LL ret = 0; 705 | while (b) { 706 | if (b & 1) { 707 | ret += a; 708 | if (ret >= m) ret -= m; 709 | } 710 | a += a; 711 | if (a >= m) a -= m; 712 | b >>= 1; 713 | } 714 | return ret; 715 | } 716 | ``` 717 | 718 | + O(1) 719 | 720 | ```cpp 721 | LL mul(LL u, LL v, LL p) { 722 | return (u * v - LL((long double) u * v / p) * p + p) % p; 723 | } 724 | LL mul(LL u, LL v, LL p) { // 卡常 725 | LL t = u * v - LL((long double) u * v / p) * p; 726 | return t < 0 ? t + p : t; 727 | } 728 | ``` 729 | 730 | ## 快速幂 731 | 732 | * 如果模数是素数,则可在函数体内加上`n %= MOD - 1;`(费马小定理)。 733 | 734 | ```cpp 735 | LL bin(LL x, LL n, LL MOD) { 736 | LL ret = MOD != 1; 737 | for (x %= MOD; n; n >>= 1, x = x * x % MOD) 738 | if (n & 1) ret = ret * x % MOD; 739 | return ret; 740 | } 741 | ``` 742 | 743 | * 防爆 LL 744 | * 前置模板:快速乘 745 | 746 | ```cpp 747 | LL bin(LL x, LL n, LL MOD) { 748 | LL ret = MOD != 1; 749 | for (x %= MOD; n; n >>= 1, x = mul(x, x, MOD)) 750 | if (n & 1) ret = mul(ret, x, MOD); 751 | return ret; 752 | } 753 | ``` 754 | 755 | ## 高斯消元 756 | 757 | * n - 方程个数,m - 变量个数, a 是 n \* \(m + 1\) 的增广矩阵,free 是否为自由变量 758 | * 返回自由变量个数,-1 无解 759 | 760 | * 浮点数版本 761 | 762 | ```cpp 763 | typedef double LD; 764 | const LD eps = 1E-10; 765 | const int maxn = 2000 + 10; 766 | 767 | int n, m; 768 | LD a[maxn][maxn], x[maxn]; 769 | bool free_x[maxn]; 770 | 771 | inline int sgn(LD x) { return (x > eps) - (x < -eps); } 772 | 773 | int gauss(LD a[maxn][maxn], int n, int m) { 774 | memset(free_x, 1, sizeof free_x); memset(x, 0, sizeof x); 775 | int r = 0, c = 0; 776 | while (r < n && c < m) { 777 | int m_r = r; 778 | FOR (i, r + 1, n) 779 | if (fabs(a[i][c]) > fabs(a[m_r][c])) m_r = i; 780 | if (m_r != r) 781 | FOR (j, c, m + 1) 782 | swap(a[r][j], a[m_r][j]); 783 | if (!sgn(a[r][c])) { 784 | a[r][c] = 0; 785 | ++c; 786 | continue; 787 | } 788 | FOR (i, r + 1, n) 789 | if (a[i][c]) { 790 | LD t = a[i][c] / a[r][c]; 791 | FOR (j, c, m + 1) a[i][j] -= a[r][j] * t; 792 | } 793 | ++r; ++c; 794 | } 795 | FOR (i, r, n) 796 | if (sgn(a[i][m])) return -1; 797 | if (r < m) { 798 | FORD (i, r - 1, -1) { 799 | int f_cnt = 0, k = -1; 800 | FOR (j, 0, m) 801 | if (sgn(a[i][j]) && free_x[j]) { 802 | ++f_cnt; 803 | k = j; 804 | } 805 | if(f_cnt > 0) continue; 806 | LD s = a[i][m]; 807 | FOR (j, 0, m) 808 | if (j != k) s -= a[i][j] * x[j]; 809 | x[k] = s / a[i][k]; 810 | free_x[k] = 0; 811 | } 812 | return m - r; 813 | } 814 | FORD (i, m - 1, -1) { 815 | LD s = a[i][m]; 816 | FOR (j, i + 1, m) 817 | s -= a[i][j] * x[j]; 818 | x[i] = s / a[i][i]; 819 | } 820 | return 0; 821 | } 822 | ``` 823 | 824 | + 数据 825 | 826 | ``` 827 | 3 4 828 | 1 1 -2 2 829 | 2 -3 5 1 830 | 4 -1 1 5 831 | 5 0 -1 7 832 | // many 833 | 834 | 3 4 835 | 1 1 -2 2 836 | 2 -3 5 1 837 | 4 -1 -1 5 838 | 5 0 -1 0 2 839 | // no 840 | 841 | 3 4 842 | 1 1 -2 2 843 | 2 -3 5 1 844 | 4 -1 1 5 845 | 5 0 1 0 7 846 | // one 847 | ``` 848 | 849 | ## 质因数分解 850 | 851 | * 前置模板:素数筛 852 | 853 | * 带指数 854 | 855 | ```cpp 856 | LL factor[30], f_sz, factor_exp[30]; 857 | void get_factor(LL x) { 858 | f_sz = 0; 859 | LL t = sqrt(x + 0.5); 860 | for (LL i = 0; pr[i] <= t; ++i) 861 | if (x % pr[i] == 0) { 862 | factor_exp[f_sz] = 0; 863 | while (x % pr[i] == 0) { 864 | x /= pr[i]; 865 | ++factor_exp[f_sz]; 866 | } 867 | factor[f_sz++] = pr[i]; 868 | } 869 | if (x > 1) { 870 | factor_exp[f_sz] = 1; 871 | factor[f_sz++] = x; 872 | } 873 | } 874 | ``` 875 | 876 | * 不带指数 877 | 878 | ```cpp 879 | LL factor[30], f_sz; 880 | void get_factor(LL x) { 881 | f_sz = 0; 882 | LL t = sqrt(x + 0.5); 883 | for (LL i = 0; pr[i] <= t; ++i) 884 | if (x % pr[i] == 0) { 885 | factor[f_sz++] = pr[i]; 886 | while (x % pr[i] == 0) x /= pr[i]; 887 | } 888 | if (x > 1) factor[f_sz++] = x; 889 | } 890 | ``` 891 | 892 | ## 原根 893 | 894 | * 前置模板:素数筛,快速幂,分解质因数 895 | * 要求 p 为质数 896 | 897 | ```cpp 898 | LL find_smallest_primitive_root(LL p) { 899 | get_factor(p - 1); 900 | FOR (i, 2, p) { 901 | bool flag = true; 902 | FOR (j, 0, f_sz) 903 | if (bin(i, (p - 1) / factor[j], p) == 1) { 904 | flag = false; 905 | break; 906 | } 907 | if (flag) return i; 908 | } 909 | assert(0); return -1; 910 | } 911 | ``` 912 | 913 | ## 公式 914 | 915 | ### 一些数论公式 916 | 917 | - 当 $x\geq\phi(p)$ 时有 $a^x\equiv a^{x \; mod \; \phi(p) + \phi(p)}\pmod p$ 918 | - $\mu^2(n)=\sum_{d^2|n} \mu(d)$ 919 | - $\sum_{d|n} \varphi(d)=n$ 920 | - $\sum_{d|n} 2^{\omega(d)}=\sigma_0(n^2)$,其中 $\omega$ 是不同素因子个数 921 | - $\sum_{d|n} \mu^2(d)=2^{\omega(d)}$ 922 | 923 | ### 一些数论函数求和的例子 924 | 925 | + $\sum_{i=1}^n i[gcd(i, n)=1] = \frac {n \varphi(n) + [n=1]}{2}$ 926 | + $\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=x]=\sum_d \mu(d) \lfloor \frac n {dx} \rfloor \lfloor \frac m {dx} \rfloor$ 927 | + $\sum_{i=1}^n \sum_{j=1}^m gcd(i, j) = \sum_{i=1}^n \sum_{j=1}^m \sum_{d|gcd(i,j)} \varphi(d) = \sum_{d} \varphi(d) \lfloor \frac nd \rfloor \lfloor \frac md \rfloor$ 928 | + $S(n)=\sum_{i=1}^n \mu(i)=1-\sum_{i=1}^n \sum_{d|i,d < i}\mu(d) \overset{t=\frac id}{=} 1-\sum_{t=2}^nS(\lfloor \frac nt \rfloor)$ 929 | + 利用 $[n=1] = \sum_{d|n} \mu(d)$ 930 | + $S(n)=\sum_{i=1}^n \varphi(i)=\sum_{i=1}^n i-\sum_{i=1}^n \sum_{d|i,d>= 1, x = pmul(x, x, MOD)) 1032 | if (n & 1) ret = pmul(ret, x, MOD); 1033 | return ret; 1034 | } 1035 | LL Legendre(LL a, LL p) { return bin(a, (p - 1) >> 1, p); } 1036 | 1037 | LL equation_solve(LL b, LL p) { 1038 | if (p == 2) return 1; 1039 | if ((Legendre(b, p) + 1) % p == 0) 1040 | return -1; 1041 | LL a; 1042 | while (true) { 1043 | a = rand() % p; 1044 | w = ((a * a - b) % p + p) % p; 1045 | if ((Legendre(w, p) + 1) % p == 0) 1046 | break; 1047 | } 1048 | return bin({a, 1}, (p + 1) >> 1, p).x; 1049 | } 1050 | 1051 | int main() { 1052 | int T; cin >> T; 1053 | while (T--) { 1054 | LL a, p; cin >> a >> p; 1055 | a = a % p; 1056 | LL x = equation_solve(a, p); 1057 | if (x == -1) { 1058 | puts("No root"); 1059 | } else { 1060 | LL y = p - x; 1061 | if (x == y) cout << x << endl; 1062 | else cout << min(x, y) << " " << max(x, y) << endl; 1063 | } 1064 | } 1065 | } 1066 | ``` 1067 | 1068 | 1069 | ## 中国剩余定理 1070 | 1071 | + 无解返回 -1 1072 | + 前置模板:扩展欧几里得 1073 | 1074 | ```cpp 1075 | LL CRT(LL *m, LL *r, LL n) { 1076 | if (!n) return 0; 1077 | LL M = m[0], R = r[0], x, y, d; 1078 | FOR (i, 1, n) { 1079 | d = ex_gcd(M, m[i], x, y); 1080 | if ((r[i] - R) % d) return -1; 1081 | x = (r[i] - R) / d * x % (m[i] / d); 1082 | // 防爆 LL 1083 | // x = mul((r[i] - R) / d, x, m[i] / d); 1084 | R += x * M; 1085 | M = M / d * m[i]; 1086 | R %= M; 1087 | } 1088 | return R >= 0 ? R : R + M; 1089 | } 1090 | 1091 | ``` 1092 | 1093 | ## 伯努利数和等幂求和 1094 | 1095 | * 预处理逆元 1096 | * 预处理组合数 1097 | * $\sum_{i=0}^n i^k = \frac{1}{k+1} \sum_{i=0}^k \binom{k+1}{i} B_{k+1-i} (n+1)^i$. 1098 | * 也可以 $\sum_{i=0}^n i^k = \frac{1}{k+1} \sum_{i=0}^k \binom{k+1}{i} B^+_{k+1-i} n^i$。区别在于 $B^+_1 =1/2$。(心态崩了) 1099 | 1100 | ```cpp 1101 | namespace Bernoulli { 1102 | const int M = 100; 1103 | LL inv[M] = {-1, 1}; 1104 | void inv_init(LL n, LL p) { 1105 | FOR (i, 2, n) 1106 | inv[i] = (p - p / i) * inv[p % i] % p; 1107 | } 1108 | 1109 | LL C[M][M]; 1110 | void init_C(int n) { 1111 | FOR (i, 0, n) { 1112 | C[i][0] = C[i][i] = 1; 1113 | FOR (j, 1, i) 1114 | C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD; 1115 | } 1116 | } 1117 | 1118 | LL B[M] = {1}; 1119 | void init() { 1120 | inv_init(M, MOD); 1121 | init_C(M); 1122 | FOR (i, 1, M - 1) { 1123 | LL& s = B[i] = 0; 1124 | FOR (j, 0, i) 1125 | s += C[i + 1][j] * B[j] % MOD; 1126 | s = (s % MOD * -inv[i + 1] % MOD + MOD) % MOD; 1127 | } 1128 | } 1129 | 1130 | LL p[M] = {1}; 1131 | LL go(LL n, LL k) { 1132 | n %= MOD; 1133 | if (k == 0) return n; 1134 | FOR (i, 1, k + 2) 1135 | p[i] = p[i - 1] * (n + 1) % MOD; 1136 | LL ret = 0; 1137 | FOR (i, 1, k + 2) 1138 | ret += C[k + 1][i] * B[k + 1 - i] % MOD * p[i] % MOD; 1139 | ret = ret % MOD * inv[k + 1] % MOD; 1140 | return ret; 1141 | } 1142 | } 1143 | ``` 1144 | 1145 | ## 单纯形 1146 | 1147 | + 要求有基本解,也就是 x 为零向量可行 1148 | + v 要初始化为 0,n 表示向量长度,m 表示约束个数 1149 | 1150 | ```cpp 1151 | // min{ b x } / max { c x } 1152 | // A x >= c / A x <= b 1153 | // x >= 0 1154 | namespace lp { 1155 | int n, m; 1156 | double a[M][N], b[M], c[N], v; 1157 | 1158 | void pivot(int l, int e) { 1159 | b[l] /= a[l][e]; 1160 | FOR (j, 0, n) if (j != e) a[l][j] /= a[l][e]; 1161 | a[l][e] = 1 / a[l][e]; 1162 | 1163 | FOR (i, 0, m) 1164 | if (i != l && fabs(a[i][e]) > 0) { 1165 | b[i] -= a[i][e] * b[l]; 1166 | FOR (j, 0, n) 1167 | if (j != e) a[i][j] -= a[i][e] * a[l][j]; 1168 | a[i][e] = -a[i][e] * a[l][e]; 1169 | } 1170 | v += c[e] * b[l]; 1171 | FOR (j, 0, n) if (j != e) c[j] -= c[e] * a[l][j]; 1172 | c[e] = -c[e] * a[l][e]; 1173 | } 1174 | double simplex() { 1175 | while (1) { 1176 | v = 0; 1177 | int e = -1, l = -1; 1178 | FOR (i, 0, n) if (c[i] > eps) { e = i; break; } 1179 | if (e == -1) return v; 1180 | double t = INF; 1181 | FOR (i, 0, m) 1182 | if (a[i][e] > eps && t > b[i] / a[i][e]) { 1183 | t = b[i] / a[i][e]; 1184 | l = i; 1185 | } 1186 | if (l == -1) return INF; 1187 | pivot(l, e); 1188 | } 1189 | } 1190 | } 1191 | ``` 1192 | 1193 | ## 离散对数 1194 | 1195 | ### BSGS 1196 | 1197 | + 模数为素数 1198 | 1199 | ```cpp 1200 | LL BSGS(LL a, LL b, LL p) { // a^x = b (mod p) 1201 | a %= p; 1202 | if (!a && !b) return 1; 1203 | if (!a) return -1; 1204 | static map mp; mp.clear(); 1205 | LL m = sqrt(p + 1.5); 1206 | LL v = 1; 1207 | FOR (i, 1, m + 1) { 1208 | v = v * a % p; 1209 | mp[v * b % p] = i; 1210 | } 1211 | LL vv = v; 1212 | FOR (i, 1, m + 1) { 1213 | auto it = mp.find(vv); 1214 | if (it != mp.end()) return i * m - it->second; 1215 | vv = vv * v % p; 1216 | } 1217 | return -1; 1218 | } 1219 | ``` 1220 | 1221 | ### exBSGS 1222 | 1223 | + 模数可以非素数 1224 | 1225 | ```cpp 1226 | LL exBSGS(LL a, LL b, LL p) { // a^x = b (mod p) 1227 | a %= p; b %= p; 1228 | if (a == 0) return b > 1 ? -1 : b == 0 && p != 1; 1229 | LL c = 0, q = 1; 1230 | while (1) { 1231 | LL g = __gcd(a, p); 1232 | if (g == 1) break; 1233 | if (b == 1) return c; 1234 | if (b % g) return -1; 1235 | ++c; b /= g; p /= g; q = a / g * q % p; 1236 | } 1237 | static map mp; mp.clear(); 1238 | LL m = sqrt(p + 1.5); 1239 | LL v = 1; 1240 | FOR (i, 1, m + 1) { 1241 | v = v * a % p; 1242 | mp[v * b % p] = i; 1243 | } 1244 | FOR (i, 1, m + 1) { 1245 | q = q * v % p; 1246 | auto it = mp.find(q); 1247 | if (it != mp.end()) return i * m - it->second + c; 1248 | } 1249 | return -1; 1250 | } 1251 | ``` 1252 | 1253 | ## 数论分块 1254 | 1255 | $f(i) = \lfloor \frac{n}{i} \rfloor=v$ 时 $i$ 的取值范围是 $[l,r]$。 1256 | 1257 | ```cpp 1258 | for (LL l = 1, v, r; l <= N; l = r + 1) { 1259 | v = N / l; r = N / v; 1260 | } 1261 | ``` 1262 | 1263 | ## 博弈 1264 | 1265 | + Nim 游戏:每轮从若干堆石子中的一堆取走若干颗。先手必胜条件为石子数量异或和非零。 1266 | + 阶梯 Nim 游戏:可以选择阶梯上某一堆中的若干颗向下推动一级,直到全部推下去。先手必胜条件是奇数阶梯的异或和非零(对于偶数阶梯的操作可以模仿)。 1267 | + Anti-SG:无法操作者胜。先手必胜的条件是: 1268 | + SG 不为 0 且某个单一游戏的 SG 大于 1 。 1269 | + SG 为 0 且没有单一游戏的 SG 大于 1。 1270 | + Every-SG:对所有单一游戏都要操作。先手必胜的条件是单一游戏中的最大 step 为奇数。 1271 | + 对于终止状态 step 为 0 1272 | + 对于 SG 为 0 的状态,step 是最大后继 step +1 1273 | + 对于 SG 非 0 的状态,step 是最小后继 step +1 1274 | + 树上删边:叶子 SG 为 0,非叶子结点为所有子结点的 SG 值加 1 后的异或和。 1275 | 1276 | 尝试: 1277 | 1278 | + 打表找规律 1279 | + 寻找一类必胜态(如对称局面) 1280 | + 直接博弈 dp 1281 | -------------------------------------------------------------------------------- /3-图论.md: -------------------------------------------------------------------------------- 1 | # 图论 2 | 3 | ## LCA 4 | 5 | + 倍增 6 | 7 | ```cpp 8 | void dfs(int u, int fa) { 9 | pa[u][0] = fa; dep[u] = dep[fa] + 1; 10 | FOR (i, 1, SP) pa[u][i] = pa[pa[u][i - 1]][i - 1]; 11 | for (int& v: G[u]) { 12 | if (v == fa) continue; 13 | dfs(v, u); 14 | } 15 | } 16 | 17 | int lca(int u, int v) { 18 | if (dep[u] < dep[v]) swap(u, v); 19 | int t = dep[u] - dep[v]; 20 | FOR (i, 0, SP) if (t & (1 << i)) u = pa[u][i]; 21 | FORD (i, SP - 1, -1) { 22 | int uu = pa[u][i], vv = pa[v][i]; 23 | if (uu != vv) { u = uu; v = vv; } 24 | } 25 | return u == v ? u : pa[u][0]; 26 | } 27 | ``` 28 | 29 | ## 网络流 30 | 31 | + 最大流 32 | 33 | ```cpp 34 | struct E { 35 | int to, cp; 36 | E(int to, int cp): to(to), cp(cp) {} 37 | }; 38 | 39 | struct Dinic { 40 | static const int M = 1E5 * 5; 41 | int m, s, t; 42 | vector edges; 43 | vector G[M]; 44 | int d[M]; 45 | int cur[M]; 46 | 47 | void init(int n, int s, int t) { 48 | this->s = s; this->t = t; 49 | for (int i = 0; i <= n; i++) G[i].clear(); 50 | edges.clear(); m = 0; 51 | } 52 | 53 | void addedge(int u, int v, int cap) { 54 | edges.emplace_back(v, cap); 55 | edges.emplace_back(u, 0); 56 | G[u].push_back(m++); 57 | G[v].push_back(m++); 58 | } 59 | 60 | bool BFS() { 61 | memset(d, 0, sizeof d); 62 | queue Q; 63 | Q.push(s); d[s] = 1; 64 | while (!Q.empty()) { 65 | int x = Q.front(); Q.pop(); 66 | for (int& i: G[x]) { 67 | E &e = edges[i]; 68 | if (!d[e.to] && e.cp > 0) { 69 | d[e.to] = d[x] + 1; 70 | Q.push(e.to); 71 | } 72 | } 73 | } 74 | return d[t]; 75 | } 76 | 77 | int DFS(int u, int cp) { 78 | if (u == t || !cp) return cp; 79 | int tmp = cp, f; 80 | for (int& i = cur[u]; i < G[u].size(); i++) { 81 | E& e = edges[G[u][i]]; 82 | if (d[u] + 1 == d[e.to]) { 83 | f = DFS(e.to, min(cp, e.cp)); 84 | e.cp -= f; 85 | edges[G[u][i] ^ 1].cp += f; 86 | cp -= f; 87 | if (!cp) break; 88 | } 89 | } 90 | return tmp - cp; 91 | } 92 | 93 | int go() { 94 | int flow = 0; 95 | while (BFS()) { 96 | memset(cur, 0, sizeof cur); 97 | flow += DFS(s, INF); 98 | } 99 | return flow; 100 | } 101 | } DC; 102 | ``` 103 | 104 | + 费用流 105 | 106 | ```cpp 107 | struct E { 108 | int from, to, cp, v; 109 | E() {} 110 | E(int f, int t, int cp, int v) : from(f), to(t), cp(cp), v(v) {} 111 | }; 112 | 113 | struct MCMF { 114 | int n, m, s, t; 115 | vector edges; 116 | vector G[M]; 117 | bool inq[M]; 118 | int d[M], p[M], a[M]; 119 | 120 | void init(int _n, int _s, int _t) { 121 | n = _n; s = _s; t = _t; 122 | FOR (i, 0, n + 1) G[i].clear(); 123 | edges.clear(); m = 0; 124 | } 125 | 126 | void addedge(int from, int to, int cap, int cost) { 127 | edges.emplace_back(from, to, cap, cost); 128 | edges.emplace_back(to, from, 0, -cost); 129 | G[from].push_back(m++); 130 | G[to].push_back(m++); 131 | } 132 | 133 | bool BellmanFord(int &flow, int &cost) { 134 | FOR (i, 0, n + 1) d[i] = INF; 135 | memset(inq, 0, sizeof inq); 136 | d[s] = 0, a[s] = INF, inq[s] = true; 137 | queue Q; Q.push(s); 138 | while (!Q.empty()) { 139 | int u = Q.front(); Q.pop(); 140 | inq[u] = false; 141 | for (int& idx: G[u]) { 142 | E &e = edges[idx]; 143 | if (e.cp && d[e.to] > d[u] + e.v) { 144 | d[e.to] = d[u] + e.v; 145 | p[e.to] = idx; 146 | a[e.to] = min(a[u], e.cp); 147 | if (!inq[e.to]) { 148 | Q.push(e.to); 149 | inq[e.to] = true; 150 | } 151 | } 152 | } 153 | } 154 | if (d[t] == INF) return false; 155 | flow += a[t]; 156 | cost += a[t] * d[t]; 157 | int u = t; 158 | while (u != s) { 159 | edges[p[u]].cp -= a[t]; 160 | edges[p[u] ^ 1].cp += a[t]; 161 | u = edges[p[u]].from; 162 | } 163 | return true; 164 | } 165 | 166 | int go() { 167 | int flow = 0, cost = 0; 168 | while (BellmanFord(flow, cost)); 169 | return cost; 170 | } 171 | } MM; 172 | ``` 173 | 174 | + zkw 费用流(代码长度没有优势) 175 | + 不允许有负权边 176 | 177 | ```cpp 178 | struct E { 179 | int to, cp, v; 180 | E() {} 181 | E(int to, int cp, int v): to(to), cp(cp), v(v) {} 182 | }; 183 | 184 | struct MCMF { 185 | int n, m, s, t, cost, D; 186 | vector edges; 187 | vector G[N]; 188 | bool vis[N]; 189 | 190 | void init(int _n, int _s, int _t) { 191 | n = _n; s = _s; t = _t; 192 | FOR (i, 0, n + 1) G[i].clear(); 193 | edges.clear(); m = 0; 194 | } 195 | 196 | void addedge(int from, int to, int cap, int cost) { 197 | edges.emplace_back(to, cap, cost); 198 | edges.emplace_back(from, 0, -cost); 199 | G[from].push_back(m++); 200 | G[to].push_back(m++); 201 | } 202 | 203 | int aug(int u, int cp) { 204 | if (u == t) { 205 | cost += D * cp; 206 | return cp; 207 | } 208 | vis[u] = true; 209 | int tmp = cp; 210 | for (int idx: G[u]) { 211 | E& e = edges[idx]; 212 | if (e.cp && !e.v && !vis[e.to]) { 213 | int f = aug(e.to, min(cp, e.cp)); 214 | e.cp -= f; 215 | edges[idx ^ 1].cp += f; 216 | cp -= f; 217 | if (!cp) break; 218 | } 219 | } 220 | return tmp - cp; 221 | } 222 | 223 | bool modlabel() { 224 | int d = INF; 225 | FOR (u, 0, n + 1) 226 | if (vis[u]) 227 | for (int& idx: G[u]) { 228 | E& e = edges[idx]; 229 | if (e.cp && !vis[e.to]) d = min(d, e.v); 230 | } 231 | if (d == INF) return false; 232 | FOR (u, 0, n + 1) 233 | if (vis[u]) 234 | for (int& idx: G[u]) { 235 | edges[idx].v -= d; 236 | edges[idx ^ 1].v += d; 237 | } 238 | D += d; 239 | return true; 240 | } 241 | 242 | int go(int k) { 243 | cost = D = 0; 244 | int flow = 0; 245 | while (true) { 246 | memset(vis, 0, sizeof vis); 247 | int t = aug(s, INF); 248 | if (!t && !modlabel()) break; 249 | flow += t; 250 | } 251 | return cost; 252 | } 253 | } MM; 254 | ``` 255 | 256 | + 带下界网络流: 257 | + 无源汇:$u \rightarrow v$ 边容量为 $[l,r]$,连容量 $r-l$,虚拟源点到 $v$ 连 $l$,$u$ 到虚拟汇点连 $l$。 258 | + 有源汇:为了让流能循环使用,连 $T \rightarrow S$,容量 $\infty$。 259 | + 最大流:跑完可行流后,加 $S' \rightarrow S$,$T \rightarrow T'$,最大流就是答案($T \rightarrow S$ 的流量自动退回去了,这一部分就是下界部分的流量)。 260 | + 最小流:$T$ 到 $S$ 的那条边的实际流量,减去删掉那条边后 $T$ 到 $S$ 的最大流。 261 | + 网上说可能会减成负的,还要有限地供应 $S$ 之后,再跑一遍 $S$ 到 $T$ 的。 262 | + 费用流:必要的部分(下界以下的)不要钱,剩下的按照最大流。 263 | 264 | ## 树上路径交 265 | 266 | ```cpp 267 | int intersection(int x, int y, int xx, int yy) { 268 | int t[4] = {lca(x, xx), lca(x, yy), lca(y, xx), lca(y, yy)}; 269 | sort(t, t + 4); 270 | int r = lca(x, y), rr = lca(xx, yy); 271 | if (dep[t[0]] < min(dep[r], dep[rr]) || dep[t[2]] < max(dep[r], dep[rr])) 272 | return 0; 273 | int tt = lca(t[2], t[3]); 274 | int ret = 1 + dep[t[2]] + dep[t[3]] - dep[tt] * 2; 275 | return ret; 276 | } 277 | ``` 278 | 279 | ## 树上点分治 280 | 281 | ```cpp 282 | int get_rt(int u) { 283 | static int q[N], fa[N], sz[N], mx[N]; 284 | int p = 0, cur = -1; 285 | q[p++] = u; fa[u] = -1; 286 | while (++cur < p) { 287 | u = q[cur]; mx[u] = 0; sz[u] = 1; 288 | for (int& v: G[u]) 289 | if (!vis[v] && v != fa[u]) fa[q[p++] = v] = u; 290 | } 291 | FORD (i, p - 1, -1) { 292 | u = q[i]; 293 | mx[u] = max(mx[u], p - sz[u]); 294 | if (mx[u] * 2 <= p) return u; 295 | sz[fa[u]] += sz[u]; 296 | mx[fa[u]] = max(mx[fa[u]], sz[u]); 297 | } 298 | assert(0); 299 | } 300 | 301 | void dfs(int u) { 302 | u = get_rt(u); 303 | vis[u] = true; 304 | get_dep(u, -1, 0); 305 | // ... 306 | for (E& e: G[u]) { 307 | int v = e.to; 308 | if (vis[v]) continue; 309 | // ... 310 | dfs(v); 311 | } 312 | } 313 | ``` 314 | 315 | + 动态点分治 316 | 317 | ```cpp 318 | const int N = 15E4 + 100, INF = 1E9; 319 | struct E { 320 | int to, d; 321 | }; 322 | vector G[N]; 323 | int n, Q, w[N]; 324 | LL A, ans; 325 | 326 | bool vis[N]; 327 | int sz[N]; 328 | 329 | int get_rt(int u) { 330 | static int q[N], fa[N], sz[N], mx[N]; 331 | int p = 0, cur = -1; 332 | q[p++] = u; fa[u] = -1; 333 | while (++cur < p) { 334 | u = q[cur]; mx[u] = 0; sz[u] = 1; 335 | for (int& v: G[u]) 336 | if (!vis[v] && v != fa[u]) fa[q[p++] = v] = u; 337 | } 338 | FORD (i, p - 1, -1) { 339 | u = q[i]; 340 | mx[u] = max(mx[u], p - sz[u]); 341 | if (mx[u] * 2 <= p) return u; 342 | sz[fa[u]] += sz[u]; 343 | mx[fa[u]] = max(mx[fa[u]], sz[u]); 344 | } 345 | assert(0); 346 | } 347 | 348 | int dep[N], md[N]; 349 | void get_dep(int u, int fa, int d) { 350 | dep[u] = d; md[u] = 0; 351 | for (E& e: G[u]) { 352 | int v = e.to; 353 | if (vis[v] || v == fa) continue; 354 | get_dep(v, u, d + e.d); 355 | md[u] = max(md[u], md[v] + 1); 356 | } 357 | } 358 | 359 | struct P { 360 | int w; 361 | LL s; 362 | }; 363 | using VP = vector

; 364 | struct R { 365 | VP *rt, *rt2; 366 | int dep; 367 | }; 368 | VP pool[N << 1], *pit = pool; 369 | vector tr[N]; 370 | 371 | void go(int u, int fa, VP* rt, VP* rt2) { 372 | tr[u].push_back({rt, rt2, dep[u]}); 373 | for (E& e: G[u]) { 374 | int v = e.to; 375 | if (v == fa || vis[v]) continue; 376 | go(v, u, rt, rt2); 377 | } 378 | } 379 | 380 | void dfs(int u) { 381 | u = get_rt(u); 382 | vis[u] = true; 383 | get_dep(u, -1, 0); 384 | VP* rt = pit++; tr[u].push_back({rt, nullptr, 0}); 385 | for (E& e: G[u]) { 386 | int v = e.to; 387 | if (vis[v]) continue; 388 | go(v, u, rt, pit++); 389 | dfs(v); 390 | } 391 | } 392 | 393 | bool cmp(const P& a, const P& b) { return a.w < b.w; } 394 | 395 | LL query(VP& p, int d, int l, int r) { 396 | l = lower_bound(p.begin(), p.end(), P{l, -1}, cmp) - p.begin(); 397 | r = upper_bound(p.begin(), p.end(), P{r, -1}, cmp) - p.begin() - 1; 398 | return p[r].s - p[l - 1].s + 1LL * (r - l + 1) * d; 399 | } 400 | 401 | int main() { 402 | cin >> n >> Q >> A; 403 | FOR (i, 1, n + 1) scanf("%d", &w[i]); 404 | FOR (_, 1, n) { 405 | int u, v, d; scanf("%d%d%d", &u, &v, &d); 406 | G[u].push_back({v, d}); G[v].push_back({u, d}); 407 | } 408 | dfs(1); 409 | FOR (i, 1, n + 1) 410 | for (R& x: tr[i]) { 411 | x.rt->push_back({w[i], x.dep}); 412 | if (x.rt2) x.rt2->push_back({w[i], x.dep}); 413 | } 414 | FOR (it, pool, pit) { 415 | it->push_back({-INF, 0}); 416 | sort(it->begin(), it->end(), cmp); 417 | FOR (i, 1, it->size()) 418 | (*it)[i].s += (*it)[i - 1].s; 419 | } 420 | while (Q--) { 421 | int u; LL a, b; scanf("%d%lld%lld", &u, &a, &b); 422 | a = (a + ans) % A; b = (b + ans) % A; 423 | int l = min(a, b), r = max(a, b); 424 | ans = 0; 425 | for (R& x: tr[u]) { 426 | ans += query(*(x.rt), x.dep, l, r); 427 | if (x.rt2) ans -= query(*(x.rt2), x.dep, l, r); 428 | } 429 | printf("%lld\n", ans); 430 | } 431 | } 432 | ``` 433 | 434 | ## 树链剖分 435 | 436 | + 初始化需要清空 `clk` 437 | + 使用 `hld::predfs(1, 1); hld::dfs(1, 1);` 438 | 439 | ```cpp 440 | int fa[N], dep[N], idx[N], out[N], ridx[N]; 441 | namespace hld { 442 | int sz[N], son[N], top[N], clk; 443 | void predfs(int u, int d) { 444 | dep[u] = d; sz[u] = 1; 445 | int& maxs = son[u] = -1; 446 | for (int& v: G[u]) { 447 | if (v == fa[u]) continue; 448 | fa[v] = u; 449 | predfs(v, d + 1); 450 | sz[u] += sz[v]; 451 | if (maxs == -1 || sz[v] > sz[maxs]) maxs = v; 452 | } 453 | } 454 | void dfs(int u, int tp) { 455 | top[u] = tp; idx[u] = ++clk; ridx[clk] = u; 456 | if (son[u] != -1) dfs(son[u], tp); 457 | for (int& v: G[u]) 458 | if (v != fa[u] && v != son[u]) dfs(v, v); 459 | out[u] = clk; 460 | } 461 | template 462 | int go(int u, int v, T&& f = [](int, int) {}) { 463 | int uu = top[u], vv = top[v]; 464 | while (uu != vv) { 465 | if (dep[uu] < dep[vv]) { swap(uu, vv); swap(u, v); } 466 | f(idx[uu], idx[u]); 467 | u = fa[uu]; uu = top[u]; 468 | } 469 | if (dep[u] < dep[v]) swap(u, v); 470 | // choose one 471 | // f(idx[v], idx[u]); 472 | // if (u != v) f(idx[v] + 1, idx[u]); 473 | return v; 474 | } 475 | int up(int u, int d) { 476 | while (d) { 477 | if (dep[u] - dep[top[u]] < d) { 478 | d -= dep[u] - dep[top[u]]; 479 | u = top[u]; 480 | } else return ridx[idx[u] - d]; 481 | u = fa[u]; --d; 482 | } 483 | return u; 484 | } 485 | int finds(int u, int rt) { // 找 u 在 rt 的哪个儿子的子树中 486 | while (top[u] != top[rt]) { 487 | u = top[u]; 488 | if (fa[u] == rt) return u; 489 | u = fa[u]; 490 | } 491 | return ridx[idx[rt] + 1]; 492 | } 493 | } 494 | ``` 495 | 496 | ## 二分图匹配 497 | 498 | + 最小覆盖数 = 最大匹配数 499 | + 最大独立集 = 顶点数 - 二分图匹配数 500 | + DAG 最小路径覆盖数 = 结点数 - 拆点后二分图最大匹配数 501 | 502 | 503 | ```cpp 504 | struct MaxMatch { 505 | int n; 506 | vector G[N]; 507 | int vis[N], left[N], clk; 508 | 509 | void init(int n) { 510 | this->n = n; 511 | FOR (i, 0, n + 1) G[i].clear(); 512 | memset(left, -1, sizeof left); 513 | memset(vis, -1, sizeof vis); 514 | } 515 | 516 | bool dfs(int u) { 517 | for (int v: G[u]) 518 | if (vis[v] != clk) { 519 | vis[v] = clk; 520 | if (left[v] == -1 || dfs(left[v])) { 521 | left[v] = u; 522 | return true; 523 | } 524 | } 525 | return false; 526 | } 527 | 528 | int match() { 529 | int ret = 0; 530 | for (clk = 0; clk <= n; ++clk) 531 | if (dfs(clk)) ++ret; 532 | return ret; 533 | } 534 | } MM; 535 | ``` 536 | 537 | + 二分图最大权完美匹配 KM ($O(n^3)$) 538 | 539 | ```cpp 540 | namespace R { 541 | const int M = 400 + 5; 542 | const int INF = 2E9; 543 | int n; 544 | int w[M][M], kx[M], ky[M], py[M], vy[M], slk[M], pre[M]; 545 | 546 | LL KM() { 547 | FOR (i, 1, n + 1) 548 | FOR (j, 1, n + 1) 549 | kx[i] = max(kx[i], w[i][j]); 550 | FOR (i, 1, n + 1) { 551 | fill(vy, vy + n + 1, 0); 552 | fill(slk, slk + n + 1, INF); 553 | fill(pre, pre + n + 1, 0); 554 | int k = 0, p = -1; 555 | for (py[k = 0] = i; py[k]; k = p) { 556 | int d = INF; 557 | vy[k] = 1; 558 | int x = py[k]; 559 | FOR (j, 1, n + 1) 560 | if (!vy[j]) { 561 | int t = kx[x] + ky[j] - w[x][j]; 562 | if (t < slk[j]) { slk[j] = t; pre[j] = k; } 563 | if (slk[j] < d) { d = slk[j]; p = j; } 564 | } 565 | FOR (j, 0, n + 1) 566 | if (vy[j]) { kx[py[j]] -= d; ky[j] += d; } 567 | else slk[j] -= d; 568 | } 569 | for (; k; k = pre[k]) py[k] = py[pre[k]]; 570 | } 571 | LL ans = 0; 572 | FOR (i, 1, n + 1) ans += kx[i] + ky[i]; 573 | return ans; 574 | } 575 | } 576 | ``` 577 | 578 | ## 虚树 579 | 580 | ```cpp 581 | void go(vector& V, int& k) { 582 | int u = V[k]; f[u] = 0; 583 | dbg(u, k); 584 | for (auto& e: G[u]) { 585 | int v = e.to; 586 | if (v == pa[u][0]) continue; 587 | while (k + 1 < V.size()) { 588 | int to = V[k + 1]; 589 | if (in[to] <= out[v]) { 590 | go(V, ++k); 591 | if (key[to]) f[u] += w[to]; 592 | else f[u] += min(f[to], (LL)w[to]); 593 | } else break; 594 | } 595 | } 596 | dbg(u, f[u]); 597 | } 598 | inline bool cmp(int a, int b) { return in[a] < in[b]; } 599 | LL solve(vector& V) { 600 | static vector a; a.clear(); 601 | for (int& x: V) a.push_back(x); 602 | sort(a.begin(), a.end(), cmp); 603 | FOR (i, 1, a.size()) 604 | a.push_back(lca(a[i], a[i - 1])); 605 | a.push_back(1); 606 | sort(a.begin(), a.end(), cmp); 607 | a.erase(unique(a.begin(), a.end()), a.end()); 608 | dbg(a); 609 | int tmp; go(a, tmp = 0); 610 | return f[1]; 611 | } 612 | ``` 613 | 614 | ## 欧拉路径 615 | 616 | ```cpp 617 | int S[N << 1], top; 618 | Edge edges[N << 1]; 619 | set G[N]; 620 | 621 | void DFS(int u) { 622 | S[top++] = u; 623 | for (int eid: G[u]) { 624 | int v = edges[eid].get_other(u); 625 | G[u].erase(eid); 626 | G[v].erase(eid); 627 | DFS(v); 628 | return; 629 | } 630 | } 631 | 632 | void fleury(int start) { 633 | int u = start; 634 | top = 0; path.clear(); 635 | S[top++] = u; 636 | while (top) { 637 | u = S[--top]; 638 | if (!G[u].empty()) 639 | DFS(u); 640 | else path.push_back(u); 641 | } 642 | } 643 | ``` 644 | 645 | ## 强连通分量与 2-SAT 646 | 647 | ```cpp 648 | int n, m; 649 | vector G[N], rG[N], vs; 650 | int used[N], cmp[N]; 651 | 652 | void add_edge(int from, int to) { 653 | G[from].push_back(to); 654 | rG[to].push_back(from); 655 | } 656 | 657 | void dfs(int v) { 658 | used[v] = true; 659 | for (int u: G[v]) { 660 | if (!used[u]) 661 | dfs(u); 662 | } 663 | vs.push_back(v); 664 | } 665 | 666 | void rdfs(int v, int k) { 667 | used[v] = true; 668 | cmp[v] = k; 669 | for (int u: rG[v]) 670 | if (!used[u]) 671 | rdfs(u, k); 672 | } 673 | 674 | int scc() { 675 | memset(used, 0, sizeof(used)); 676 | vs.clear(); 677 | for (int v = 0; v < n; ++v) 678 | if (!used[v]) dfs(v); 679 | memset(used, 0, sizeof(used)); 680 | int k = 0; 681 | for (int i = (int) vs.size() - 1; i >= 0; --i) 682 | if (!used[vs[i]]) rdfs(vs[i], k++); 683 | return k; 684 | } 685 | 686 | int main() { 687 | cin >> n >> m; 688 | n *= 2; 689 | for (int i = 0; i < m; ++i) { 690 | int a, b; cin >> a >> b; 691 | add_edge(a - 1, (b - 1) ^ 1); 692 | add_edge(b - 1, (a - 1) ^ 1); 693 | } 694 | scc(); 695 | for (int i = 0; i < n; i += 2) { 696 | if (cmp[i] == cmp[i + 1]) { 697 | puts("NIE"); 698 | return 0; 699 | } 700 | } 701 | for (int i = 0; i < n; i += 2) { 702 | if (cmp[i] > cmp[i + 1]) printf("%d\n", i + 1); 703 | else printf("%d\n", i + 2); 704 | } 705 | } 706 | ``` 707 | 708 | ## 拓扑排序 709 | 710 | ```cpp 711 | vector toporder(int n) { 712 | vector orders; 713 | queue q; 714 | for (int i = 0; i < n; i++) 715 | if (!deg[i]) { 716 | q.push(i); 717 | orders.push_back(i); 718 | } 719 | while (!q.empty()) { 720 | int u = q.front(); q.pop(); 721 | for (int v: G[u]) 722 | if (!--deg[v]) { 723 | q.push(v); 724 | orders.push_back(v); 725 | } 726 | } 727 | return orders; 728 | } 729 | ``` 730 | 731 | ## 一般图匹配 732 | 733 | 带花树。复杂度 $O(n^3)$。 734 | 735 | ```cpp 736 | int n; 737 | vector G[N]; 738 | int fa[N], mt[N], pre[N], mk[N]; 739 | int lca_clk, lca_mk[N]; 740 | pair ce[N]; 741 | 742 | void connect(int u, int v) { 743 | mt[u] = v; 744 | mt[v] = u; 745 | } 746 | int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } 747 | 748 | void flip(int s, int u) { 749 | if (s == u) return; 750 | if (mk[u] == 2) { 751 | int v1 = ce[u].first, v2 = ce[u].second; 752 | flip(mt[u], v1); 753 | flip(s, v2); 754 | connect(v1, v2); 755 | } else { 756 | flip(s, pre[mt[u]]); 757 | connect(pre[mt[u]], mt[u]); 758 | } 759 | } 760 | 761 | int get_lca(int u, int v) { 762 | lca_clk++; 763 | for (u = find(u), v = find(v); ; u = find(pre[u]), v = find(pre[v])) { 764 | if (u && lca_mk[u] == lca_clk) return u; 765 | lca_mk[u] = lca_clk; 766 | if (v && lca_mk[v] == lca_clk) return v; 767 | lca_mk[v] = lca_clk; 768 | } 769 | } 770 | 771 | void access(int u, int p, const pair& c, vector& q) { 772 | for (u = find(u); u != p; u = find(pre[u])) { 773 | if (mk[u] == 2) { 774 | ce[u] = c; 775 | q.push_back(u); 776 | } 777 | fa[find(u)] = find(p); 778 | } 779 | } 780 | 781 | bool aug(int s) { 782 | fill(mk, mk + n + 1, 0); 783 | fill(pre, pre + n + 1, 0); 784 | iota(fa, fa + n + 1, 0); 785 | vector q = {s}; 786 | mk[s] = 1; 787 | int t = 0; 788 | for (int t = 0; t < (int) q.size(); ++t) { 789 | // q size can be changed 790 | int u = q[t]; 791 | for (int &v: G[u]) { 792 | if (find(v) == find(u)) continue; 793 | if (!mk[v] && !mt[v]) { 794 | flip(s, u); 795 | connect(u, v); 796 | return true; 797 | } else if (!mk[v]) { 798 | int w = mt[v]; 799 | mk[v] = 2; mk[w] = 1; 800 | pre[w] = v; pre[v] = u; 801 | q.push_back(w); 802 | } else if (mk[find(v)] == 1) { 803 | int p = get_lca(u, v); 804 | access(u, p, {u, v}, q); 805 | access(v, p, {v, u}, q); 806 | } 807 | } 808 | } 809 | return false; 810 | } 811 | 812 | int match() { 813 | fill(mt + 1, mt + n + 1, 0); 814 | lca_clk = 0; 815 | int ans = 0; 816 | FOR (i, 1, n + 1) 817 | if (!mt[i]) ans += aug(i); 818 | return ans; 819 | } 820 | 821 | int main() { 822 | int m; cin >> n >> m; 823 | while (m--) { 824 | int u, v; scanf("%d%d", &u, &v); 825 | G[u].push_back(v); G[v].push_back(u); 826 | } 827 | printf("%d\n", match()); 828 | FOR (i, 1, n + 1) printf("%d%c", mt[i], i == _i - 1 ? '\n' : ' '); 829 | return 0; 830 | } 831 | 832 | ``` 833 | 834 | ## Tarjan 835 | 836 | ### 割点 837 | 838 | + 判断割点 839 | + 注意原图可能不连通 840 | 841 | ```cpp 842 | int dfn[N], low[N], clk; 843 | void init() { clk = 0; memset(dfn, 0, sizeof dfn); } 844 | void tarjan(int u, int fa) { 845 | low[u] = dfn[u] = ++clk; 846 | int cc = fa != -1; 847 | for (int& v: G[u]) { 848 | if (v == fa) continue; 849 | if (!dfn[v]) { 850 | tarjan(v, u); 851 | low[u] = min(low[u], low[v]); 852 | cc += low[v] >= dfn[u]; 853 | } else low[u] = min(low[u], dfn[v]); 854 | } 855 | if (cc > 1) // ... 856 | } 857 | ``` 858 | 859 | ### 桥 860 | 861 | + 注意原图不连通和重边 862 | 863 | ```cpp 864 | int dfn[N], low[N], clk; 865 | void init() { memset(dfn, 0, sizeof dfn); clk = 0; } 866 | void tarjan(int u, int fa) { 867 | low[u] = dfn[u] = ++clk; 868 | int _fst = 0; 869 | for (E& e: G[u]) { 870 | int v = e.to; if (v == fa && ++_fst == 1) continue; 871 | if (!dfn[v]) { 872 | tarjan(v, u); 873 | if (low[v] > dfn[u]) // ... 874 | low[u] = min(low[u], low[v]); 875 | } else low[u] = min(low[u], dfn[v]); 876 | } 877 | } 878 | ``` 879 | 880 | ### 强连通分量缩点 881 | 882 | ```cpp 883 | int low[N], dfn[N], clk, B, bl[N]; 884 | vector bcc[N]; 885 | void init() { B = clk = 0; memset(dfn, 0, sizeof dfn); } 886 | void tarjan(int u) { 887 | static int st[N], p; 888 | static bool in[N]; 889 | dfn[u] = low[u] = ++clk; 890 | st[p++] = u; in[u] = true; 891 | for (int& v: G[u]) { 892 | if (!dfn[v]) { 893 | tarjan(v); 894 | low[u] = min(low[u], low[v]); 895 | } else if (in[v]) low[u] = min(low[u], dfn[v]); 896 | } 897 | if (dfn[u] == low[u]) { 898 | while (1) { 899 | int x = st[--p]; in[x] = false; 900 | bl[x] = B; bcc[B].push_back(x); 901 | if (x == u) break; 902 | } 903 | ++B; 904 | } 905 | } 906 | ``` 907 | 908 | ### 点双连通分量 / 广义圆方树 909 | 910 | + 数组开两倍 911 | + 一条边也被计入点双了(适合拿来建圆方树),可以用 点数 <= 边数 过滤 912 | 913 | ```cpp 914 | struct E { int to, nxt; } e[N]; 915 | int hd[N], ecnt; 916 | void addedge(int u, int v) { 917 | e[ecnt] = {v, hd[u]}; 918 | hd[u] = ecnt++; 919 | } 920 | int low[N], dfn[N], clk, B, bno[N]; 921 | vector bc[N], be[N]; 922 | bool vise[N]; 923 | void init() { 924 | memset(vise, 0, sizeof vise); 925 | memset(hd, -1, sizeof hd); 926 | memset(dfn, 0, sizeof dfn); 927 | memset(bno, -1, sizeof bno); 928 | B = clk = ecnt = 0; 929 | } 930 | 931 | void tarjan(int u, int feid) { 932 | static int st[N], p; 933 | static auto add = [&](int x) { 934 | if (bno[x] != B) { bno[x] = B; bc[B].push_back(x); } 935 | }; 936 | low[u] = dfn[u] = ++clk; 937 | for (int i = hd[u]; ~i; i = e[i].nxt) { 938 | if ((feid ^ i) == 1) continue; 939 | if (!vise[i]) { st[p++] = i; vise[i] = vise[i ^ 1] = true; } 940 | int v = e[i].to; 941 | if (!dfn[v]) { 942 | tarjan(v, i); 943 | low[u] = min(low[u], low[v]); 944 | if (low[v] >= dfn[u]) { 945 | bc[B].clear(); be[B].clear(); 946 | while (1) { 947 | int eid = st[--p]; 948 | add(e[eid].to); add(e[eid ^ 1].to); 949 | be[B].push_back(eid); 950 | if ((eid ^ i) <= 1) break; 951 | } 952 | ++B; 953 | } 954 | } else low[u] = min(low[u], dfn[v]); 955 | } 956 | } 957 | ``` 958 | 959 | ## 圆方树 960 | 961 | + 从仙人掌建圆方树 962 | + N 至少边数 × 2 963 | 964 | ```cpp 965 | vector G[N]; 966 | int nn; 967 | 968 | struct E { int to, nxt; }; 969 | namespace C { 970 | E e[N * 2]; 971 | int hd[N], ecnt; 972 | void addedge(int u, int v) { 973 | e[ecnt] = {v, hd[u]}; 974 | hd[u] = ecnt++; 975 | } 976 | int idx[N], clk, fa[N]; 977 | bool ring[N]; 978 | void init() { ecnt = 0; memset(hd, -1, sizeof hd); clk = 0; } 979 | void dfs(int u, int feid) { 980 | idx[u] = ++clk; 981 | for (int i = hd[u]; ~i; i = e[i].nxt) { 982 | if ((i ^ feid) == 1) continue; 983 | int v = e[i].to; 984 | if (!idx[v]) { 985 | fa[v] = u; ring[u] = false; 986 | dfs(v, i); 987 | if (!ring[u]) { G[u].push_back(v); G[v].push_back(u); } 988 | } else if (idx[v] < idx[u]) { 989 | ++nn; 990 | G[nn].push_back(v); G[v].push_back(nn); // 强行把环的根放在最前面 991 | for (int x = u; x != v; x = fa[x]) { 992 | ring[x] = true; 993 | G[nn].push_back(x); G[x].push_back(nn); 994 | } 995 | ring[v] = true; 996 | } 997 | } 998 | } 999 | } 1000 | ``` 1001 | 1002 | ## 最小树形图 1003 | 1004 | 会篡改边。 1005 | 1006 | ```cpp 1007 | vector edges; 1008 | int in[N], id[N], pre[N], vis[N]; 1009 | // a copy of n is needed 1010 | LL zl_tree(int rt, int n) { 1011 | LL ans = 0; 1012 | int v, _n = n; 1013 | while (1) { 1014 | fill(in, in + n, INF); 1015 | for (E &e: edges) { 1016 | if (e.u != e.v && e.w < in[e.v]) { 1017 | pre[e.v] = e.u; 1018 | in[e.v] = e.w; 1019 | } 1020 | } 1021 | FOR (i, 0, n) if (i != rt && in[i] == INF) return -1; 1022 | int tn = 0; 1023 | fill(id, id + _n, -1); fill(vis, vis + _n, -1); 1024 | in[rt] = 0; 1025 | FOR (i, 0, n) { 1026 | ans += in[v = i]; 1027 | while (vis[v] != i && id[v] == -1 && v != rt) { 1028 | vis[v] = i; v = pre[v]; 1029 | } 1030 | if (v != rt && id[v] == -1) { 1031 | for (int u = pre[v]; u != v; u = pre[u]) id[u] = tn; 1032 | id[v] = tn++; 1033 | } 1034 | } 1035 | if (tn == 0) break; 1036 | FOR (i, 0, n) if (id[i] == -1) id[i] = tn++; 1037 | for (int i = 0; i < (int) edges.size(); ) { 1038 | auto &e = edges[i]; 1039 | v = e.v; 1040 | e.u = id[e.u]; e.v = id[e.v]; 1041 | if (e.u != e.v) { e.w -= in[v]; i++; } 1042 | else { swap(e, edges.back()); edges.pop_back(); } 1043 | } 1044 | n = tn; rt = id[rt]; 1045 | } 1046 | return ans; 1047 | } 1048 | ``` 1049 | 1050 | ## 差分约束 1051 | 1052 | 一个系统 $n$ 个变量和 $m$ 个约束条件组成,每个约束条件形如 $x_j-x_i \le b_k$。可以发现每个约束条件都形如最短路中的三角不等式 $d_u-d_v \le w_{u,v}$。因此连一条边 $(i,j,b_k)$ 建图。 1053 | 1054 | 若要使得所有量两两的值最接近,源点到各点的距离初始成 $0$,跑最远路。 1055 | 1056 | 若要使得某一变量与其他变量的差尽可能大,则源点到各点距离初始化成 $\infty$,跑最短路。 1057 | 1058 | ## 三元环、四元环 1059 | 1060 | ### 四元环 1061 | 1062 | 考虑这样一个四元环,将答案统计在度数最大的点 $b$ 上。考虑枚举点 $u$,然后枚举与其相邻的点 $v$,然后再枚举所有度数比 $v$ 大的与 $v$ 相邻的点,这些点显然都可能作为 $b$ 点,我们维护一个计数器来计算之前 $b$ 被枚举多少次,答案加上计数器的值,然后计数器加一。 1063 | 1064 | 枚举完 $u$ 之后,我们用和枚举时一样的方法来清空计数器就好了。 1065 | 1066 | 任何一个点,与其直接相连的度数大于等于它的点最多只有 $\sqrt{2m}$ 个。所以复杂度 $O(m \sqrt{m})$。 1067 | 1068 | ```cpp 1069 | LL cycle4() { 1070 | LL ans = 0; 1071 | iota(kth, kth + n + 1, 0); 1072 | sort(kth, kth + n, [&](int x, int y) { return deg[x] < deg[y]; }); 1073 | FOR (i, 1, n + 1) rk[kth[i]] = i; 1074 | FOR (u, 1, n + 1) 1075 | for (int v: G[u]) 1076 | if (rk[v] > rk[u]) key[u].push_back(v); 1077 | FOR (u, 1, n + 1) { 1078 | for (int v: G[u]) 1079 | for (int w: key[v]) 1080 | if (rk[w] > rk[u]) ans += cnt[w]++; 1081 | for (int v: G[u]) 1082 | for (int w: key[v]) 1083 | if (rk[w] > rk[u]) --cnt[w]; 1084 | } 1085 | return ans; 1086 | } 1087 | ``` 1088 | 1089 | ### 三元环 1090 | 1091 | 将点分成度入小于 $\sqrt{m}$ 和超过 $\sqrt{m}$ 的两类。现求包含第一类点的三元环个数。由于边数较少,直接枚举两条边即可。由于一个点度数不超过 $\sqrt{m}$,所以一条边最多被枚举 $\sqrt{m}$ 次,复杂度 $O(m \sqrt{m})$。再求不包含第一类点的三元环个数,由于这样的点不超过 $\sqrt{m}$ 个,所以复杂度也是 $O(m \sqrt{m})$。 1092 | 1093 | 对于每条无向边 $(u,v)$,如果 $d_u < d_v$,那么连有向边 $(u,v)$,否则有向边 $(v,u)$。度数相等的按第二关键字判断。然后枚举每个点 $x$,假设 $x$ 是三元组中度数最小的点,然后暴力往后面枚举两条边找到 $y$,判断 $(x,y)$ 是否有边即可。复杂度也是 $O(m \sqrt{m})$。 1094 | 1095 | ```cpp 1096 | int cycle3() { 1097 | int ans = 0; 1098 | for (E &e: edges) { deg[e.u]++; deg[e.v]++; } 1099 | for (E &e: edges) { 1100 | if (deg[e.u] < deg[e.v] || (deg[e.u] == deg[e.v] && e.u < e.v)) 1101 | G[e.u].push_back(e.v); 1102 | else G[e.v].push_back(e.u); 1103 | } 1104 | FOR (x, 1, n + 1) { 1105 | for (int y: G[x]) p[y] = x; 1106 | for (int y: G[x]) for (int z: G[y]) if (p[z] == x) ans++; 1107 | } 1108 | return ans; 1109 | } 1110 | ``` 1111 | 1112 | ## 支配树 1113 | 1114 | + `semi[x]` 半必经点(就是 $x$ 的祖先 $z$ 中,能不经过 $z$ 和 $x$ 之间的树上的点而到达 $x$ 的点中深度最小的) 1115 | + `idom[x]` 最近必经点(就是深度最大的根到 $x$ 的必经点) 1116 | 1117 | ```cpp 1118 | vector G[N], rG[N]; 1119 | vector dt[N]; 1120 | 1121 | namespace tl{ 1122 | int fa[N], idx[N], clk, ridx[N]; 1123 | int c[N], best[N], semi[N], idom[N]; 1124 | void init(int n) { 1125 | clk = 0; 1126 | fill(c, c + n + 1, -1); 1127 | FOR (i, 1, n + 1) dt[i].clear(); 1128 | FOR (i, 1, n + 1) semi[i] = best[i] = i; 1129 | fill(idx, idx + n + 1, 0); 1130 | } 1131 | void dfs(int u) { 1132 | idx[u] = ++clk; ridx[clk] = u; 1133 | for (int& v: G[u]) if (!idx[v]) { fa[v] = u; dfs(v); } 1134 | } 1135 | int fix(int x) { 1136 | if (c[x] == -1) return x; 1137 | int &f = c[x], rt = fix(f); 1138 | if (idx[semi[best[x]]] > idx[semi[best[f]]]) best[x] = best[f]; 1139 | return f = rt; 1140 | } 1141 | void go(int rt) { 1142 | dfs(rt); 1143 | FORD (i, clk, 1) { 1144 | int x = ridx[i], mn = clk + 1; 1145 | for (int& u: rG[x]) { 1146 | if (!idx[u]) continue; // 可能不能到达所有点 1147 | fix(u); mn = min(mn, idx[semi[best[u]]]); 1148 | } 1149 | c[x] = fa[x]; 1150 | dt[semi[x] = ridx[mn]].push_back(x); 1151 | x = ridx[i - 1]; 1152 | for (int& u: dt[x]) { 1153 | fix(u); 1154 | if (semi[best[u]] != x) idom[u] = best[u]; 1155 | else idom[u] = x; 1156 | } 1157 | dt[x].clear(); 1158 | } 1159 | 1160 | FOR (i, 2, clk + 1) { 1161 | int u = ridx[i]; 1162 | if (idom[u] != semi[u]) idom[u] = idom[idom[u]]; 1163 | dt[idom[u]].push_back(u); 1164 | } 1165 | } 1166 | } 1167 | ``` 1168 | 1169 | -------------------------------------------------------------------------------- /4-计算几何.md: -------------------------------------------------------------------------------- 1 | # 计算几何 2 | 3 | ## 二维几何:点与向量 4 | 5 | ```cpp 6 | #define y1 yy1 7 | #define nxt(i) ((i + 1) % s.size()) 8 | typedef double LD; 9 | const LD PI = 3.14159265358979323846; 10 | const LD eps = 1E-10; 11 | int sgn(LD x) { return fabs(x) < eps ? 0 : (x > 0 ? 1 : -1); } 12 | struct L; 13 | struct P; 14 | typedef P V; 15 | struct P { 16 | LD x, y; 17 | explicit P(LD x = 0, LD y = 0): x(x), y(y) {} 18 | explicit P(const L& l); 19 | }; 20 | struct L { 21 | P s, t; 22 | L() {} 23 | L(P s, P t): s(s), t(t) {} 24 | }; 25 | 26 | P operator + (const P& a, const P& b) { return P(a.x + b.x, a.y + b.y); } 27 | P operator - (const P& a, const P& b) { return P(a.x - b.x, a.y - b.y); } 28 | P operator * (const P& a, LD k) { return P(a.x * k, a.y * k); } 29 | P operator / (const P& a, LD k) { return P(a.x / k, a.y / k); } 30 | inline bool operator < (const P& a, const P& b) { 31 | return sgn(a.x - b.x) < 0 || (sgn(a.x - b.x) == 0 && sgn(a.y - b.y) < 0); 32 | } 33 | bool operator == (const P& a, const P& b) { return !sgn(a.x - b.x) && !sgn(a.y - b.y); } 34 | P::P(const L& l) { *this = l.t - l.s; } 35 | ostream &operator << (ostream &os, const P &p) { 36 | return (os << "(" << p.x << "," << p.y << ")"); 37 | } 38 | istream &operator >> (istream &is, P &p) { 39 | return (is >> p.x >> p.y); 40 | } 41 | 42 | LD dist(const P& p) { return sqrt(p.x * p.x + p.y * p.y); } 43 | LD dot(const V& a, const V& b) { return a.x * b.x + a.y * b.y; } 44 | LD det(const V& a, const V& b) { return a.x * b.y - a.y * b.x; } 45 | LD cross(const P& s, const P& t, const P& o = P()) { return det(s - o, t - o); } 46 | // -------------------------------------------- 47 | ``` 48 | 49 | ### 象限 50 | 51 | ```cpp 52 | // 象限 53 | int quad(P p) { 54 | int x = sgn(p.x), y = sgn(p.y); 55 | if (x > 0 && y >= 0) return 1; 56 | if (x <= 0 && y > 0) return 2; 57 | if (x < 0 && y <= 0) return 3; 58 | if (x >= 0 && y < 0) return 4; 59 | assert(0); 60 | } 61 | 62 | // 仅适用于参照点在所有点一侧的情况 63 | struct cmp_angle { 64 | P p; 65 | bool operator () (const P& a, const P& b) { 66 | // int qa = quad(a - p), qb = quad(b - p); 67 | // if (qa != qb) return qa < qb; 68 | int d = sgn(cross(a, b, p)); 69 | if (d) return d > 0; 70 | return dist(a - p) < dist(b - p); 71 | } 72 | }; 73 | ``` 74 | 75 | ### 线 76 | 77 | ```cpp 78 | // 是否平行 79 | bool parallel(const L& a, const L& b) { 80 | return !sgn(det(P(a), P(b))); 81 | } 82 | // 直线是否相等 83 | bool l_eq(const L& a, const L& b) { 84 | return parallel(a, b) && parallel(L(a.s, b.t), L(b.s, a.t)); 85 | } 86 | // 逆时针旋转 r 弧度 87 | P rotation(const P& p, const LD& r) { return P(p.x * cos(r) - p.y * sin(r), p.x * sin(r) + p.y * cos(r)); } 88 | P RotateCCW90(const P& p) { return P(-p.y, p.x); } 89 | P RotateCW90(const P& p) { return P(p.y, -p.x); } 90 | // 单位法向量 91 | V normal(const V& v) { return V(-v.y, v.x) / dist(v); } 92 | ``` 93 | 94 | ### 点与线 95 | 96 | ```cpp 97 | // 点在线段上 <= 0包含端点 < 0 则不包含 98 | bool p_on_seg(const P& p, const L& seg) { 99 | P a = seg.s, b = seg.t; 100 | return !sgn(det(p - a, b - a)) && sgn(dot(p - a, p - b)) <= 0; 101 | } 102 | // 点到直线距离 103 | LD dist_to_line(const P& p, const L& l) { 104 | return fabs(cross(l.s, l.t, p)) / dist(l); 105 | } 106 | // 点到线段距离 107 | LD dist_to_seg(const P& p, const L& l) { 108 | if (l.s == l.t) return dist(p - l); 109 | V vs = p - l.s, vt = p - l.t; 110 | if (sgn(dot(l, vs)) < 0) return dist(vs); 111 | else if (sgn(dot(l, vt)) > 0) return dist(vt); 112 | else return dist_to_line(p, l); 113 | } 114 | ``` 115 | 116 | ### 线与线 117 | 118 | ```cpp 119 | // 求直线交 需要事先保证有界 120 | P l_intersection(const L& a, const L& b) { 121 | LD s1 = det(P(a), b.s - a.s), s2 = det(P(a), b.t - a.s); 122 | return (b.s * s2 - b.t * s1) / (s2 - s1); 123 | } 124 | // 向量夹角的弧度 125 | LD angle(const V& a, const V& b) { 126 | LD r = asin(fabs(det(a, b)) / dist(a) / dist(b)); 127 | if (sgn(dot(a, b)) < 0) r = PI - r; 128 | return r; 129 | } 130 | // 线段和直线是否有交 1 = 规范,2 = 不规范 131 | int s_l_cross(const L& seg, const L& line) { 132 | int d1 = sgn(cross(line.s, line.t, seg.s)); 133 | int d2 = sgn(cross(line.s, line.t, seg.t)); 134 | if ((d1 ^ d2) == -2) return 1; // proper 135 | if (d1 == 0 || d2 == 0) return 2; 136 | return 0; 137 | } 138 | // 线段的交 1 = 规范,2 = 不规范 139 | int s_cross(const L& a, const L& b, P& p) { 140 | int d1 = sgn(cross(a.t, b.s, a.s)), d2 = sgn(cross(a.t, b.t, a.s)); 141 | int d3 = sgn(cross(b.t, a.s, b.s)), d4 = sgn(cross(b.t, a.t, b.s)); 142 | if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) { p = l_intersection(a, b); return 1; } 143 | if (!d1 && p_on_seg(b.s, a)) { p = b.s; return 2; } 144 | if (!d2 && p_on_seg(b.t, a)) { p = b.t; return 2; } 145 | if (!d3 && p_on_seg(a.s, b)) { p = a.s; return 2; } 146 | if (!d4 && p_on_seg(a.t, b)) { p = a.t; return 2; } 147 | return 0; 148 | } 149 | ``` 150 | 151 | ## 多边形 152 | 153 | ### 面积、凸包 154 | 155 | ```cpp 156 | typedef vector

S; 157 | 158 | // 点是否在多边形中 0 = 在外部 1 = 在内部 -1 = 在边界上 159 | int inside(const S& s, const P& p) { 160 | int cnt = 0; 161 | FOR (i, 0, s.size()) { 162 | P a = s[i], b = s[nxt(i)]; 163 | if (p_on_seg(p, L(a, b))) return -1; 164 | if (sgn(a.y - b.y) <= 0) swap(a, b); 165 | if (sgn(p.y - a.y) > 0) continue; 166 | if (sgn(p.y - b.y) <= 0) continue; 167 | cnt += sgn(cross(b, a, p)) > 0; 168 | } 169 | return bool(cnt & 1); 170 | } 171 | // 多边形面积,有向面积可能为负 172 | LD polygon_area(const S& s) { 173 | LD ret = 0; 174 | FOR (i, 1, (LL)s.size() - 1) 175 | ret += cross(s[i], s[i + 1], s[0]); 176 | return ret / 2; 177 | } 178 | // 构建凸包 点不可以重复 < 0 边上可以有点, <= 0 则不能 179 | // 会改变输入点的顺序 180 | const int MAX_N = 1000; 181 | S convex_hull(S& s) { 182 | // assert(s.size() >= 3); 183 | sort(s.begin(), s.end()); 184 | S ret(MAX_N * 2); 185 | int sz = 0; 186 | FOR (i, 0, s.size()) { 187 | while (sz > 1 && sgn(cross(ret[sz - 1], s[i], ret[sz - 2])) < 0) --sz; 188 | ret[sz++] = s[i]; 189 | } 190 | int k = sz; 191 | FORD (i, (LL)s.size() - 2, -1) { 192 | while (sz > k && sgn(cross(ret[sz - 1], s[i], ret[sz - 2])) < 0) --sz; 193 | ret[sz++] = s[i]; 194 | } 195 | ret.resize(sz - (s.size() > 1)); 196 | return ret; 197 | } 198 | 199 | P ComputeCentroid(const vector

&p) { 200 | P c(0, 0); 201 | LD scale = 6.0 * polygon_area(p); 202 | for (unsigned i = 0; i < p.size(); i++) { 203 | unsigned j = (i + 1) % p.size(); 204 | c = c + (p[i] + p[j]) * (p[i].x * p[j].y - p[j].x * p[i].y); 205 | } 206 | return c / scale; 207 | } 208 | ``` 209 | 210 | ### 旋转卡壳 211 | 212 | ```cpp 213 | LD rotatingCalipers(vector

& qs) { 214 | int n = qs.size(); 215 | if (n == 2) 216 | return dist(qs[0] - qs[1]); 217 | int i = 0, j = 0; 218 | FOR (k, 0, n) { 219 | if (!(qs[i] < qs[k])) i = k; 220 | if (qs[j] < qs[k]) j = k; 221 | } 222 | LD res = 0; 223 | int si = i, sj = j; 224 | while (i != sj || j != si) { 225 | res = max(res, dist(qs[i] - qs[j])); 226 | if (sgn(cross(qs[(i+1)%n] - qs[i], qs[(j+1)%n] - qs[j])) < 0) 227 | i = (i + 1) % n; 228 | else j = (j + 1) % n; 229 | } 230 | return res; 231 | } 232 | 233 | int main() { 234 | int n; 235 | while (cin >> n) { 236 | S v(n); 237 | FOR (i, 0, n) cin >> v[i].x >> v[i].y; 238 | convex_hull(v); 239 | printf("%.0f\n", rotatingCalipers(v)); 240 | } 241 | } 242 | ``` 243 | 244 | ### 半平面交 245 | 246 | ```cpp 247 | struct LV { 248 | P p, v; LD ang; 249 | LV() {} 250 | LV(P s, P t): p(s), v(t - s) { ang = atan2(v.y, v.x); } 251 | }; // 另一种向量表示 252 | 253 | bool operator < (const LV &a, const LV& b) { return a.ang < b.ang; } 254 | bool on_left(const LV& l, const P& p) { return sgn(cross(l.v, p - l.p)) >= 0; } 255 | P l_intersection(const LV& a, const LV& b) { 256 | P u = a.p - b.p; LD t = cross(b.v, u) / cross(a.v, b.v); 257 | return a.p + a.v * t; 258 | } 259 | 260 | S half_plane_intersection(vector& L) { 261 | int n = L.size(), fi, la; 262 | sort(L.begin(), L.end()); 263 | vector

p(n); vector q(n); 264 | q[fi = la = 0] = L[0]; 265 | FOR (i, 1, n) { 266 | while (fi < la && !on_left(L[i], p[la - 1])) la--; 267 | while (fi < la && !on_left(L[i], p[fi])) fi++; 268 | q[++la] = L[i]; 269 | if (sgn(cross(q[la].v, q[la - 1].v)) == 0) { 270 | la--; 271 | if (on_left(q[la], L[i].p)) q[la] = L[i]; 272 | } 273 | if (fi < la) p[la - 1] = l_intersection(q[la - 1], q[la]); 274 | } 275 | while (fi < la && !on_left(q[fi], p[la - 1])) la--; 276 | if (la - fi <= 1) return vector

(); 277 | p[la] = l_intersection(q[la], q[fi]); 278 | return vector

(p.begin() + fi, p.begin() + la + 1); 279 | } 280 | 281 | S convex_intersection(const vector

&v1, const vector

&v2) { 282 | vector h; int n = v1.size(), m = v2.size(); 283 | FOR (i, 0, n) h.push_back(LV(v1[i], v1[(i + 1) % n])); 284 | FOR (i, 0, m) h.push_back(LV(v2[i], v2[(i + 1) % m])); 285 | return half_plane_intersection(h); 286 | } 287 | ``` 288 | 289 | ## 圆 290 | 291 | ```cpp 292 | struct C { 293 | P p; LD r; 294 | C(LD x = 0, LD y = 0, LD r = 0): p(x, y), r(r) {} 295 | C(P p, LD r): p(p), r(r) {} 296 | }; 297 | ``` 298 | 299 | ### 三点求圆心 300 | 301 | ```cpp 302 | P compute_circle_center(P a, P b, P c) { 303 | b = (a + b) / 2; 304 | c = (a + c) / 2; 305 | return l_intersection({b, b + RotateCW90(a - b)}, {c , c + RotateCW90(a - c)}); 306 | } 307 | ``` 308 | 309 | ### 圆线交点、圆圆交点 310 | 311 | + 圆和线的交点关于圆心是顺时针的 312 | 313 | ```cpp 314 | vector

c_l_intersection(const L& l, const C& c) { 315 | vector

ret; 316 | P b(l), a = l.s - c.p; 317 | LD x = dot(b, b), y = dot(a, b), z = dot(a, a) - c.r * c.r; 318 | LD D = y * y - x * z; 319 | if (sgn(D) < 0) return ret; 320 | ret.push_back(c.p + a + b * (-y + sqrt(D + eps)) / x); 321 | if (sgn(D) > 0) ret.push_back(c.p + a + b * (-y - sqrt(D)) / x); 322 | return ret; 323 | } 324 | 325 | vector

c_c_intersection(C a, C b) { 326 | vector

ret; 327 | LD d = dist(a.p - b.p); 328 | if (sgn(d) == 0 || sgn(d - (a.r + b.r)) > 0 || sgn(d + min(a.r, b.r) - max(a.r, b.r)) < 0) 329 | return ret; 330 | LD x = (d * d - b.r * b.r + a.r * a.r) / (2 * d); 331 | LD y = sqrt(a.r * a.r - x * x); 332 | P v = (b.p - a.p) / d; 333 | ret.push_back(a.p + v * x + RotateCCW90(v) * y); 334 | if (sgn(y) > 0) ret.push_back(a.p + v * x - RotateCCW90(v) * y); 335 | return ret; 336 | } 337 | ``` 338 | 339 | ### 圆圆位置关系 340 | 341 | ```cpp 342 | // 1:内含 2:内切 3:相交 4:外切 5:相离 343 | int c_c_relation(const C& a, const C& v) { 344 | LD d = dist(a.p - v.p); 345 | if (sgn(d - a.r - v.r) > 0) return 5; 346 | if (sgn(d - a.r - v.r) == 0) return 4; 347 | LD l = fabs(a.r - v.r); 348 | if (sgn(d - l) > 0) return 3; 349 | if (sgn(d - l) == 0) return 2; 350 | if (sgn(d - l) < 0) return 1; 351 | } 352 | ``` 353 | 354 | ### 圆与多边形交 355 | 356 | + HDU 5130 357 | + 注意顺时针逆时针(可能要取绝对值) 358 | 359 | ```cpp 360 | LD sector_area(const P& a, const P& b, LD r) { 361 | LD th = atan2(a.y, a.x) - atan2(b.y, b.x); 362 | while (th <= 0) th += 2 * PI; 363 | while (th > 2 * PI) th -= 2 * PI; 364 | th = min(th, 2 * PI - th); 365 | return r * r * th / 2; 366 | } 367 | 368 | LD c_tri_area(P a, P b, P center, LD r) { 369 | a = a - center; b = b - center; 370 | int ina = sgn(dist(a) - r) < 0, inb = sgn(dist(b) - r) < 0; 371 | // dbg(a, b, ina, inb); 372 | if (ina && inb) { 373 | return fabs(cross(a, b)) / 2; 374 | } else { 375 | auto p = c_l_intersection(L(a, b), C(0, 0, r)); 376 | if (ina ^ inb) { 377 | auto cr = p_on_seg(p[0], L(a, b)) ? p[0] : p[1]; 378 | if (ina) return sector_area(b, cr, r) + fabs(cross(a, cr)) / 2; 379 | else return sector_area(a, cr, r) + fabs(cross(b, cr)) / 2; 380 | } else { 381 | if ((int) p.size() == 2 && p_on_seg(p[0], L(a, b))) { 382 | if (dist(p[0] - a) > dist(p[1] - a)) swap(p[0], p[1]); 383 | return sector_area(a, p[0], r) + sector_area(p[1], b, r) 384 | + fabs(cross(p[0], p[1])) / 2; 385 | } else return sector_area(a, b, r); 386 | } 387 | } 388 | } 389 | 390 | typedef vector

S; 391 | LD c_poly_area(S poly, const C& c) { 392 | LD ret = 0; int n = poly.size(); 393 | FOR (i, 0, n) { 394 | int t = sgn(cross(poly[i] - c.p, poly[(i + 1) % n] - c.p)); 395 | if (t) ret += t * c_tri_area(poly[i], poly[(i + 1) % n], c.p, c.r); 396 | } 397 | return ret; 398 | } 399 | ``` 400 | 401 | ### 圆的离散化、面积并 402 | 403 | SPOJ: CIRU, EOJ: 284 404 | 405 | + 版本 1:复杂度 $O(n^3 \log n)$。虽然常数小,但还是难以接受。 406 | + 优点?想不出来。 407 | + 原理上是用竖线进行切分,然后对每一个切片分别计算。 408 | + 扫描线部分可以魔改,求各种东西。 409 | 410 | ```cpp 411 | inline LD rt(LD x) { return sgn(x) == 0 ? 0 : sqrt(x); } 412 | inline LD sq(LD x) { return x * x; } 413 | 414 | // 圆弧 415 | // 如果按照 x 离散化,圆弧是 "横着的" 416 | // 记录圆弧的左端点、右端点、中点的坐标,和圆弧所在的圆 417 | // 调用构造要保证 c.x - x.r <= xl < xr <= c.y + x.r 418 | // t = 1 下圆弧 t = -1 上圆弧 419 | struct CV { 420 | LD yl, yr, ym; C o; int type; 421 | CV() {} 422 | CV(LD yl, LD yr, LD ym, C c, int t) 423 | : yl(yl), yr(yr), ym(ym), type(t), o(c) {} 424 | }; 425 | 426 | // 辅助函数 求圆上纵坐标 427 | pair c_point_eval(const C& c, LD x) { 428 | LD d = fabs(c.p.x - x), h = rt(sq(c.r) - sq(d)); 429 | return {c.p.y - h, c.p.y + h}; 430 | } 431 | // 构造上下圆弧 432 | pair pairwise_curves(const C& c, LD xl, LD xr) { 433 | LD yl1, yl2, yr1, yr2, ym1, ym2; 434 | tie(yl1, yl2) = c_point_eval(c, xl); 435 | tie(ym1, ym2) = c_point_eval(c, (xl + xr) / 2); 436 | tie(yr1, yr2) = c_point_eval(c, xr); 437 | return {CV(yl1, yr1, ym1, c, 1), CV(yl2, yr2, ym2, c, -1)}; 438 | } 439 | 440 | // 离散化之后同一切片内的圆弧应该是不相交的 441 | bool operator < (const CV& a, const CV& b) { return a.ym < b.ym; } 442 | // 计算圆弧和连接圆弧端点的线段构成的封闭图形的面积 443 | LD cv_area(const CV& v, LD xl, LD xr) { 444 | LD l = rt(sq(xr - xl) + sq(v.yr - v.yl)); 445 | LD d = rt(sq(v.o.r) - sq(l / 2)); 446 | LD ang = atan(l / d / 2); 447 | return ang * sq(v.o.r) - d * l / 2; 448 | } 449 | 450 | LD circle_union(const vector& cs) { 451 | int n = cs.size(); 452 | vector xs; 453 | FOR (i, 0, n) { 454 | xs.push_back(cs[i].p.x - cs[i].r); 455 | xs.push_back(cs[i].p.x); 456 | xs.push_back(cs[i].p.x + cs[i].r); 457 | FOR (j, i + 1, n) { 458 | auto pts = c_c_intersection(cs[i], cs[j]); 459 | for (auto& p: pts) xs.push_back(p.x); 460 | } 461 | } 462 | sort(xs.begin(), xs.end()); 463 | xs.erase(unique(xs.begin(), xs.end(), [](LD x, LD y) { return sgn(x - y) == 0; }), xs.end()); 464 | LD ans = 0; 465 | FOR (i, 0, (int) xs.size() - 1) { 466 | LD xl = xs[i], xr = xs[i + 1]; 467 | vector intv; 468 | FOR (k, 0, n) { 469 | auto& c = cs[k]; 470 | if (sgn(c.p.x - c.r - xl) <= 0 && sgn(c.p.x + c.r - xr) >= 0) { 471 | auto t = pairwise_curves(c, xl, xr); 472 | intv.push_back(t.first); intv.push_back(t.second); 473 | } 474 | } 475 | sort(intv.begin(), intv.end()); 476 | 477 | vector areas(intv.size()); 478 | FOR (i, 0, intv.size()) areas[i] = cv_area(intv[i], xl, xr); 479 | 480 | int cc = 0; 481 | FOR (i, 0, intv.size()) { 482 | if (cc > 0) { 483 | ans += (intv[i].yl - intv[i - 1].yl + intv[i].yr - intv[i - 1].yr) * (xr - xl) / 2; 484 | ans += intv[i - 1].type * areas[i - 1]; 485 | ans -= intv[i].type * areas[i]; 486 | } 487 | cc += intv[i].type; 488 | } 489 | } 490 | return ans; 491 | } 492 | ``` 493 | 494 | + 版本 2:复杂度 $O(n^2 \log n)$。 495 | + 原理是:认为所求部分是一个奇怪的多边形 + 若干弓形。然后对于每个圆分别求贡献的弓形,并累加多边形有向面积。 496 | + 同样可以魔改扫描线的部分,用于求周长、至少覆盖 $k$ 次等等。 497 | + 内含、内切、同一个圆的情况,通常需要特殊处理。 498 | + 下面的代码是 $k$ 圆覆盖。 499 | 500 | ```cpp 501 | inline LD angle(const P& p) { return atan2(p.y, p.x); } 502 | 503 | // 圆弧上的点 504 | // p 是相对于圆心的坐标 505 | // a 是在圆上的 atan2 [-PI, PI] 506 | struct CP { 507 | P p; LD a; int t; 508 | CP() {} 509 | CP(P p, LD a, int t): p(p), a(a), t(t) {} 510 | }; 511 | bool operator < (const CP& u, const CP& v) { return u.a < v.a; } 512 | LD cv_area(LD r, const CP& q1, const CP& q2) { 513 | return (r * r * (q2.a - q1.a) - cross(q1.p, q2.p)) / 2; 514 | } 515 | 516 | LD ans[N]; 517 | void circle_union(const vector& cs) { 518 | int n = cs.size(); 519 | FOR (i, 0, n) { 520 | // 有相同的圆的话只考虑第一次出现 521 | bool ok = true; 522 | FOR (j, 0, i) 523 | if (sgn(cs[i].r - cs[j].r) == 0 && cs[i].p == cs[j].p) { 524 | ok = false; 525 | break; 526 | } 527 | if (!ok) continue; 528 | auto& c = cs[i]; 529 | vector ev; 530 | int belong_to = 0; 531 | P bound = c.p + P(-c.r, 0); 532 | ev.emplace_back(bound, -PI, 0); 533 | ev.emplace_back(bound, PI, 0); 534 | FOR (j, 0, n) { 535 | if (i == j) continue; 536 | if (c_c_relation(c, cs[j]) <= 2) { 537 | if (sgn(cs[j].r - c.r) >= 0) // 完全被另一个圆包含,等于说叠了一层 538 | belong_to++; 539 | continue; 540 | } 541 | auto its = c_c_intersection(c, cs[j]); 542 | if (its.size() == 2) { 543 | P p = its[1] - c.p, q = its[0] - c.p; 544 | LD a = angle(p), b = angle(q); 545 | if (sgn(a - b) > 0) { 546 | ev.emplace_back(p, a, 1); 547 | ev.emplace_back(bound, PI, -1); 548 | ev.emplace_back(bound, -PI, 1); 549 | ev.emplace_back(q, b, -1); 550 | } else { 551 | ev.emplace_back(p, a, 1); 552 | ev.emplace_back(q, b, -1); 553 | } 554 | } 555 | } 556 | sort(ev.begin(), ev.end()); 557 | int cc = ev[0].t; 558 | FOR (j, 1, ev.size()) { 559 | int t = cc + belong_to; 560 | ans[t] += cross(ev[j - 1].p + c.p, ev[j].p + c.p) / 2; 561 | ans[t] += cv_area(c.r, ev[j - 1], ev[j]); 562 | cc += ev[j].t; 563 | } 564 | } 565 | } 566 | ``` 567 | 568 | ### 最小圆覆盖 569 | 570 | + 随机增量。期望复杂度 $O(n)$。 571 | 572 | ```cpp 573 | P compute_circle_center(P a, P b) { return (a + b) / 2; } 574 | bool p_in_circle(const P& p, const C& c) { 575 | return sgn(dist(p - c.p) - c.r) <= 0; 576 | } 577 | C min_circle_cover(const vector

&in) { 578 | vector

a(in.begin(), in.end()); 579 | dbg(a.size()); 580 | random_shuffle(a.begin(), a.end()); 581 | P c = a[0]; LD r = 0; int n = a.size(); 582 | FOR (i, 1, n) if (!p_in_circle(a[i], {c, r})) { 583 | c = a[i]; r = 0; 584 | FOR (j, 0, i) if (!p_in_circle(a[j], {c, r})) { 585 | c = compute_circle_center(a[i], a[j]); 586 | r = dist(a[j] - c); 587 | FOR (k, 0, j) if (!p_in_circle(a[k], {c, r})) { 588 | c = compute_circle_center(a[i], a[j], a[k]); 589 | r = dist(a[k] - c); 590 | } 591 | } 592 | } 593 | return {c, r}; 594 | } 595 | ``` 596 | 597 | ### 圆的反演 598 | 599 | ```cpp 600 | C inv(C c, const P& o) { 601 | LD d = dist(c.p - o); 602 | assert(sgn(d) != 0); 603 | LD a = 1 / (d - c.r); 604 | LD b = 1 / (d + c.r); 605 | c.r = (a - b) / 2 * R2; 606 | c.p = o + (c.p - o) * ((a + b) * R2 / 2 / d); 607 | return c; 608 | } 609 | ``` 610 | 611 | ## 三维计算几何 612 | 613 | ```cpp 614 | struct P; 615 | struct L; 616 | typedef P V; 617 | 618 | struct P { 619 | LD x, y, z; 620 | explicit P(LD x = 0, LD y = 0, LD z = 0): x(x), y(y), z(z) {} 621 | explicit P(const L& l); 622 | }; 623 | 624 | struct L { 625 | P s, t; 626 | L() {} 627 | L(P s, P t): s(s), t(t) {} 628 | }; 629 | 630 | struct F { 631 | P a, b, c; 632 | F() {} 633 | F(P a, P b, P c): a(a), b(b), c(c) {} 634 | }; 635 | 636 | P operator + (const P& a, const P& b) { return P(a.x + b.x, a.y + b.y, a.z + b.z); } 637 | P operator - (const P& a, const P& b) { return P(a.x - b.x, a.y - b.y, a.z - b.z); } 638 | P operator * (const P& a, LD k) { return P(a.x * k, a.y * k, a.z * k); } 639 | P operator / (const P& a, LD k) { return P(a.x / k, a.y / k, a.z / k); } 640 | inline int operator < (const P& a, const P& b) { 641 | return sgn(a.x - b.x) < 0 || (sgn(a.x - b.x) == 0 && (sgn(a.y - b.y) < 0 || 642 | (sgn(a.y - b.y) == 0 && sgn(a.z - b.z) < 0))); 643 | } 644 | bool operator == (const P& a, const P& b) { return !sgn(a.x - b.x) && !sgn(a.y - b.y) && !sgn(a.z - b.z); } 645 | P::P(const L& l) { *this = l.t - l.s; } 646 | ostream &operator << (ostream &os, const P &p) { 647 | return (os << "(" << p.x << "," << p.y << "," << p.z << ")"); 648 | } 649 | istream &operator >> (istream &is, P &p) { 650 | return (is >> p.x >> p.y >> p.z); 651 | } 652 | 653 | // -------------------------------------------- 654 | LD dist2(const P& p) { return p.x * p.x + p.y * p.y + p.z * p.z; } 655 | LD dist(const P& p) { return sqrt(dist2(p)); } 656 | LD dot(const V& a, const V& b) { return a.x * b.x + a.y * b.y + a.z * b.z; } 657 | P cross(const P& v, const P& w) { 658 | return P(v.y * w.z - v.z * w.y, v.z * w.x - v.x * w.z, v.x * w.y - v.y * w.x); 659 | } 660 | LD mix(const V& a, const V& b, const V& c) { return dot(a, cross(b, c)); } 661 | ``` 662 | 663 | ### 旋转 664 | 665 | ```cpp 666 | // 逆时针旋转 r 弧度 667 | // axis = 0 绕 x 轴 668 | // axis = 1 绕 y 轴 669 | // axis = 2 绕 z 轴 670 | P rotation(const P& p, const LD& r, int axis = 0) { 671 | if (axis == 0) 672 | return P(p.x, p.y * cos(r) - p.z * sin(r), p.y * sin(r) + p.z * cos(r)); 673 | else if (axis == 1) 674 | return P(p.z * cos(r) - p.x * sin(r), p.y, p.z * sin(r) + p.x * cos(r)); 675 | else if (axis == 2) 676 | return P(p.x * cos(r) - p.y * sin(r), p.x * sin(r) + p.y * cos(r), p.z); 677 | } 678 | // n 是单位向量 表示旋转轴 679 | // 模板是顺时针的 680 | P rotation(const P& p, const LD& r, const P& n) { 681 | LD c = cos(r), s = sin(r), x = n.x, y = n.y, z = n.z; 682 | // dbg(c, s); 683 | return P((x * x * (1 - c) + c) * p.x + (x * y * (1 - c) + z * s) * p.y + (x * z * (1 - c) - y * s) * p.z, 684 | (x * y * (1 - c) - z * s) * p.x + (y * y * (1 - c) + c) * p.y + (y * z * (1 - c) + x * s) * p.z, 685 | (x * z * (1 - c) + y * s) * p.x + (y * z * (1 - c) - x * s) * p.y + (z * z * (1 - c) + c) * p.z); 686 | } 687 | ``` 688 | 689 | ### 线、面 690 | 691 | 函数相互依赖,所以交织在一起了。 692 | 693 | ```cpp 694 | // 点在线段上 <= 0包含端点 < 0 则不包含 695 | bool p_on_seg(const P& p, const L& seg) { 696 | P a = seg.s, b = seg.t; 697 | return !sgn(dist2(cross(p - a, b - a))) && sgn(dot(p - a, p - b)) <= 0; 698 | } 699 | // 点到直线距离 700 | LD dist_to_line(const P& p, const L& l) { 701 | return dist(cross(l.s - p, l.t - p)) / dist(l); 702 | } 703 | // 点到线段距离 704 | LD dist_to_seg(const P& p, const L& l) { 705 | if (l.s == l.t) return dist(p - l.s); 706 | V vs = p - l.s, vt = p - l.t; 707 | if (sgn(dot(l, vs)) < 0) return dist(vs); 708 | else if (sgn(dot(l, vt)) > 0) return dist(vt); 709 | else return dist_to_line(p, l); 710 | } 711 | 712 | P norm(const F& f) { return cross(f.a - f.b, f.b - f.c); } 713 | int p_on_plane(const F& f, const P& p) { return sgn(dot(norm(f), p - f.a)) == 0; } 714 | 715 | // 判两点在线段异侧 点在线段上返回 0 不共面无意义 716 | int opposite_side(const P& u, const P& v, const L& l) { 717 | return sgn(dot(cross(P(l), u - l.s), cross(P(l), v - l.s))) < 0; 718 | } 719 | 720 | bool parallel(const L& a, const L& b) { return !sgn(dist2(cross(P(a), P(b)))); } 721 | // 线段相交 722 | int s_intersect(const L& u, const L& v) { 723 | return p_on_plane(F(u.s, u.t, v.s), v.t) && 724 | opposite_side(u.s, u.t, v) && 725 | opposite_side(v.s, v.t, u); 726 | } 727 | ``` 728 | 729 | ### 凸包 730 | 731 | 增量法。先将所有的点打乱顺序,然后选择四个不共面的点组成一个四面体,如果找不到说明凸包不存在。然后遍历剩余的点,不断更新凸包。对遍历到的点做如下处理。 732 | 733 | 1. 如果点在凸包内,则不更新。 734 | 2. 如果点在凸包外,那么找到所有原凸包上所有分隔了对于这个点可见面和不可见面的边,以这样的边的两个点和新的点创建新的面加入凸包中。 735 | 736 | ```cpp 737 | 738 | struct FT { 739 | int a, b, c; 740 | FT() { } 741 | FT(int a, int b, int c) : a(a), b(b), c(c) { } 742 | }; 743 | 744 | bool p_on_line(const P& p, const L& l) { 745 | return !sgn(dist2(cross(p - l.s, P(l)))); 746 | } 747 | 748 | vector convex_hull(vector

&p) { 749 | sort(p.begin(), p.end()); 750 | p.erase(unique(p.begin(), p.end()), p.end()); 751 | random_shuffle(p.begin(), p.end()); 752 | vector face; 753 | FOR (i, 2, p.size()) { 754 | if (p_on_line(p[i], L(p[0], p[1]))) continue; 755 | swap(p[i], p[2]); 756 | FOR (j, i + 1, p.size()) 757 | if (sgn(mix(p[1] - p[0], p[2] - p[1], p[j] - p[0]))) { 758 | swap(p[j], p[3]); 759 | face.emplace_back(0, 1, 2); 760 | face.emplace_back(0, 2, 1); 761 | goto found; 762 | } 763 | } 764 | found: 765 | vector> mk(p.size(), vector(p.size())); 766 | FOR (v, 3, p.size()) { 767 | vector tmp; 768 | FOR (i, 0, face.size()) { 769 | int a = face[i].a, b = face[i].b, c = face[i].c; 770 | if (sgn(mix(p[a] - p[v], p[b] - p[v], p[c] - p[v])) < 0) { 771 | mk[a][b] = mk[b][a] = v; 772 | mk[b][c] = mk[c][b] = v; 773 | mk[c][a] = mk[a][c] = v; 774 | } else tmp.push_back(face[i]); 775 | } 776 | face = tmp; 777 | FOR (i, 0, tmp.size()) { 778 | int a = face[i].a, b = face[i].b, c = face[i].c; 779 | if (mk[a][b] == v) face.emplace_back(b, a, v); 780 | if (mk[b][c] == v) face.emplace_back(c, b, v); 781 | if (mk[c][a] == v) face.emplace_back(a, c, v); 782 | } 783 | } 784 | vector out; 785 | FOR (i, 0, face.size()) 786 | out.emplace_back(p[face[i].a], p[face[i].b], p[face[i].c]); 787 | return out; 788 | } 789 | ``` 790 | 791 | -------------------------------------------------------------------------------- /5-字符串.md: -------------------------------------------------------------------------------- 1 | # 字符串 2 | 3 | ## 后缀自动机 4 | 5 | ![](assets/sam.png) 6 | 7 | + 广义后缀自动机如果直接使用以下代码的话会产生一些冗余状态(置 last 为 1),所以要用拓扑排序。用 len 基数排序不能。 8 | + 字符集大的话要使用`map`。 9 | + 树上 dp 时注意边界(root 和 null)。 10 | + rsort 中的数组 a 是拓扑序 [1, sz) 11 | 12 | ```cpp 13 | namespace sam { 14 | const int M = N << 1; 15 | int t[M][26], len[M] = {-1}, fa[M], sz = 2, last = 1; 16 | void init() { memset(t, 0, (sz + 10) * sizeof t[0]); sz = 2; last = 1; } 17 | void ins(int ch) { 18 | int p = last, np = last = sz++; 19 | len[np] = len[p] + 1; 20 | for (; p && !t[p][ch]; p = fa[p]) t[p][ch] = np; 21 | if (!p) { fa[np] = 1; return; } 22 | int q = t[p][ch]; 23 | if (len[p] + 1 == len[q]) fa[np] = q; 24 | else { 25 | int nq = sz++; len[nq] = len[p] + 1; 26 | memcpy(t[nq], t[q], sizeof t[0]); 27 | fa[nq] = fa[q]; 28 | fa[np] = fa[q] = nq; 29 | for (; t[p][ch] == q; p = fa[p]) t[p][ch] = nq; 30 | } 31 | } 32 | 33 | int c[M] = {1}, a[M]; 34 | void rsort() { 35 | FOR (i, 1, sz) c[i] = 0; 36 | FOR (i, 1, sz) c[len[i]]++; 37 | FOR (i, 1, sz) c[i] += c[i - 1]; 38 | FOR (i, 1, sz) a[--c[len[i]]] = i; 39 | } 40 | } 41 | ``` 42 | 43 | + 真·广义后缀自动机 44 | 45 | ```cpp 46 | int t[M][26], len[M] = {-1}, fa[M], sz = 2, last = 1; 47 | LL cnt[M][2]; 48 | void ins(int ch, int id) { 49 | int p = last, np = 0, nq = 0, q = -1; 50 | if (!t[p][ch]) { 51 | np = sz++; 52 | len[np] = len[p] + 1; 53 | for (; p && !t[p][ch]; p = fa[p]) t[p][ch] = np; 54 | } 55 | if (!p) fa[np] = 1; 56 | else { 57 | q = t[p][ch]; 58 | if (len[p] + 1 == len[q]) fa[np] = q; 59 | else { 60 | nq = sz++; len[nq] = len[p] + 1; 61 | memcpy(t[nq], t[q], sizeof t[0]); 62 | fa[nq] = fa[q]; 63 | fa[np] = fa[q] = nq; 64 | for (; t[p][ch] == q; p = fa[p]) t[p][ch] = nq; 65 | } 66 | } 67 | last = np ? np : nq ? nq : q; 68 | cnt[last][id] = 1; 69 | } 70 | ``` 71 | 72 | 73 | + 按字典序建立后缀树 注意逆序插入 74 | + rsort2 里的 a 不是拓扑序,需要拓扑序就去树上做 75 | 76 | ```cpp 77 | void ins(int ch, int pp) { 78 | int p = last, np = last = sz++; 79 | len[np] = len[p] + 1; one[np] = pos[np] = pp; 80 | for (; p && !t[p][ch]; p = fa[p]) t[p][ch] = np; 81 | if (!p) { fa[np] = 1; return; } 82 | int q = t[p][ch]; 83 | if (len[q] == len[p] + 1) fa[np] = q; 84 | else { 85 | int nq = sz++; len[nq] = len[p] + 1; one[nq] = one[q]; 86 | memcpy(t[nq], t[q], sizeof t[0]); 87 | fa[nq] = fa[q]; 88 | fa[q] = fa[np] = nq; 89 | for (; p && t[p][ch] == q; p = fa[p]) t[p][ch] = nq; 90 | } 91 | } 92 | 93 | int up[M], c[256] = {2}, a[M]; 94 | void rsort2() { 95 | FOR (i, 1, 256) c[i] = 0; 96 | FOR (i, 2, sz) up[i] = s[one[i] + len[fa[i]]]; 97 | FOR (i, 2, sz) c[up[i]]++; 98 | FOR (i, 1, 256) c[i] += c[i - 1]; 99 | FOR (i, 2, sz) a[--c[up[i]]] = i; 100 | FOR (i, 2, sz) G[fa[a[i]]].push_back(a[i]); 101 | } 102 | ``` 103 | 104 | + 广义后缀自动机建后缀树,必须反向插入 105 | 106 | ```cpp 107 | int t[M][26], len[M] = {0}, fa[M], sz = 2, last = 1; 108 | char* one[M]; 109 | void ins(int ch, char* pp) { 110 | int p = last, np = 0, nq = 0, q = -1; 111 | if (!t[p][ch]) { 112 | np = sz++; one[np] = pp; 113 | len[np] = len[p] + 1; 114 | for (; p && !t[p][ch]; p = fa[p]) t[p][ch] = np; 115 | } 116 | if (!p) fa[np] = 1; 117 | else { 118 | q = t[p][ch]; 119 | if (len[p] + 1 == len[q]) fa[np] = q; 120 | else { 121 | nq = sz++; len[nq] = len[p] + 1; one[nq] = one[q]; 122 | memcpy(t[nq], t[q], sizeof t[0]); 123 | fa[nq] = fa[q]; 124 | fa[np] = fa[q] = nq; 125 | for (; t[p][ch] == q; p = fa[p]) t[p][ch] = nq; 126 | } 127 | } 128 | last = np ? np : nq ? nq : q; 129 | } 130 | int up[M], c[256] = {2}, aa[M]; 131 | vector G[M]; 132 | void rsort() { 133 | FOR (i, 1, 256) c[i] = 0; 134 | FOR (i, 2, sz) up[i] = *(one[i] + len[fa[i]]); 135 | FOR (i, 2, sz) c[up[i]]++; 136 | FOR (i, 1, 256) c[i] += c[i - 1]; 137 | FOR (i, 2, sz) aa[--c[up[i]]] = i; 138 | FOR (i, 2, sz) G[fa[aa[i]]].push_back(aa[i]); 139 | } 140 | ``` 141 | 142 | + 匹配 143 | 144 | ```cpp 145 | int u = 1, l = 0; 146 | FOR (i, 0, strlen(s)) { 147 | int ch = s[i] - 'a'; 148 | while (u && !t[u][ch]) { u = fa[u]; l = len[u]; } 149 | ++l; u = t[u][ch]; 150 | if (!u) u = 1; 151 | if (l) // do something... 152 | } 153 | ``` 154 | 155 | + 获取子串状态 156 | ```cpp 157 | int get_state(int l, int r) { 158 | int u = rpos[r], s = r - l + 1; 159 | FORD (i, SP - 1, -1) if (len[pa[u][i]] >= s) u = pa[u][i]; 160 | return u; 161 | } 162 | ``` 163 | 164 | + 配合 LCT 165 | 166 | ```cpp 167 | namespace lct_sam { 168 | extern struct P *const null; 169 | const int M = N; 170 | struct P { 171 | P *fa, *ls, *rs; 172 | int last; 173 | 174 | bool has_fa() { return fa->ls == this || fa->rs == this; } 175 | bool d() { return fa->ls == this; } 176 | P*& c(bool x) { return x ? ls : rs; } 177 | P* up() { return this; } 178 | void down() { 179 | if (ls != null) ls->last = last; 180 | if (rs != null) rs->last = last; 181 | } 182 | void all_down() { if (has_fa()) fa->all_down(); down(); } 183 | } *const null = new P{0, 0, 0, 0}, pool[M], *pit = pool; 184 | P* G[N]; 185 | int t[M][26], len[M] = {-1}, fa[M], sz = 2, last = 1; 186 | 187 | void rot(P* o) { 188 | bool dd = o->d(); 189 | P *f = o->fa, *t = o->c(!dd); 190 | if (f->has_fa()) f->fa->c(f->d()) = o; o->fa = f->fa; 191 | if (t != null) t->fa = f; f->c(dd) = t; 192 | o->c(!dd) = f->up(); f->fa = o; 193 | } 194 | void splay(P* o) { 195 | o->all_down(); 196 | while (o->has_fa()) { 197 | if (o->fa->has_fa()) 198 | rot(o->d() ^ o->fa->d() ? o : o->fa); 199 | rot(o); 200 | } 201 | o->up(); 202 | } 203 | void access(int last, P* u, P* v = null) { 204 | if (u == null) { v->last = last; return; } 205 | splay(u); 206 | P *t = u; 207 | while (t->ls != null) t = t->ls; 208 | int L = len[fa[t - pool]] + 1, R = len[u - pool]; 209 | 210 | if (u->last) bit::add(u->last - R + 2, u->last - L + 2, 1); 211 | else bit::add(1, 1, R - L + 1); 212 | bit::add(last - R + 2, last - L + 2, -1); 213 | 214 | u->rs = v; 215 | access(last, u->up()->fa, u); 216 | } 217 | void insert(P* u, P* v, P* t) { 218 | if (v != null) { splay(v); v->rs = null; } 219 | splay(u); 220 | u->fa = t; t->fa = v; 221 | } 222 | 223 | void ins(int ch, int pp) { 224 | int p = last, np = last = sz++; 225 | len[np] = len[p] + 1; 226 | for (; p && !t[p][ch]; p = fa[p]) t[p][ch] = np; 227 | if (!p) fa[np] = 1; 228 | else { 229 | int q = t[p][ch]; 230 | if (len[p] + 1 == len[q]) { fa[np] = q; G[np]->fa = G[q]; } 231 | else { 232 | int nq = sz++; len[nq] = len[p] + 1; 233 | memcpy(t[nq], t[q], sizeof t[0]); 234 | insert(G[q], G[fa[q]], G[nq]); 235 | G[nq]->last = G[q]->last; 236 | fa[nq] = fa[q]; 237 | fa[np] = fa[q] = nq; 238 | G[np]->fa = G[nq]; 239 | for (; t[p][ch] == q; p = fa[p]) t[p][ch] = nq; 240 | } 241 | } 242 | access(pp + 1, G[np]); 243 | } 244 | 245 | void init() { 246 | ++pit; 247 | FOR (i, 1, N) { 248 | G[i] = pit++; 249 | G[i]->ls = G[i]->rs = G[i]->fa = null; 250 | } 251 | G[1] = null; 252 | } 253 | } 254 | ``` 255 | 256 | ## 回文自动机 257 | 258 | + num 是该结点表示的前缀的回文后缀个数 259 | + cnt 是该结点表示的回文串在原串中的出现次数(使用前需要向父亲更新) 260 | 261 | ```cpp 262 | namespace pam { 263 | int t[N][26], fa[N], len[N], rs[N], cnt[N], num[N]; 264 | int sz, n, last; 265 | int _new(int l) { 266 | len[sz] = l; cnt[sz] = num[sz] = 0; 267 | return sz++; 268 | } 269 | void init() { 270 | memset(t, 0, sz * sizeof t[0]); 271 | rs[n = sz = 0] = -1; 272 | last = _new(0); 273 | fa[last] = _new(-1); 274 | } 275 | int get_fa(int x) { 276 | while (rs[n - 1 - len[x]] != rs[n]) x = fa[x]; 277 | return x; 278 | } 279 | void ins(int ch) { 280 | rs[++n] = ch; 281 | int p = get_fa(last); 282 | if (!t[p][ch]) { 283 | int np = _new(len[p] + 2); 284 | num[np] = num[fa[np] = t[get_fa(fa[p])][ch]] + 1; 285 | t[p][ch] = np; 286 | } 287 | ++cnt[last = t[p][ch]]; 288 | } 289 | } 290 | ``` 291 | 292 | ## manacher 293 | 294 | ```cpp 295 | int RL[N]; 296 | void manacher(int* a, int n) { // "abc" => "#a#b#a#" 297 | int r = 0, p = 0; 298 | FOR (i, 0, n) { 299 | if (i < r) RL[i] = min(RL[2 * p - i], r - i); 300 | else RL[i] = 1; 301 | while (i - RL[i] >= 0 && i + RL[i] < n && a[i - RL[i]] == a[i + RL[i]]) 302 | RL[i]++; 303 | if (RL[i] + i - 1 > r) { r = RL[i] + i - 1; p = i; } 304 | } 305 | FOR (i, 0, n) --RL[i]; 306 | } 307 | 308 | ``` 309 | 310 | ## 哈希 311 | 312 | 内置了自动双哈希开关(小心 TLE)。 313 | 314 | ```cpp 315 | #include 316 | using namespace std; 317 | 318 | #define ENABLE_DOUBLE_HASH 319 | 320 | typedef long long LL; 321 | typedef unsigned long long ULL; 322 | 323 | const int x = 135; 324 | const int N = 4e5 + 10; 325 | const int p1 = 1e9 + 7, p2 = 1e9 + 9; 326 | ULL xp1[N], xp2[N], xp[N]; 327 | 328 | void init_xp() { 329 | xp1[0] = xp2[0] = xp[0] = 1; 330 | for (int i = 1; i < N; ++i) { 331 | xp1[i] = xp1[i - 1] * x % p1; 332 | xp2[i] = xp2[i - 1] * x % p2; 333 | xp[i] = xp[i - 1] * x; 334 | } 335 | } 336 | 337 | struct String { 338 | char s[N]; 339 | int length, subsize; 340 | bool sorted; 341 | ULL h[N], hl[N]; 342 | 343 | ULL hash() { 344 | length = strlen(s); 345 | ULL res1 = 0, res2 = 0; 346 | h[length] = 0; // ATTENTION! 347 | for (int j = length - 1; j >= 0; --j) { 348 | #ifdef ENABLE_DOUBLE_HASH 349 | res1 = (res1 * x + s[j]) % p1; 350 | res2 = (res2 * x + s[j]) % p2; 351 | h[j] = (res1 << 32) | res2; 352 | #else 353 | res1 = res1 * x + s[j]; 354 | h[j] = res1; 355 | #endif 356 | // printf("%llu\n", h[j]); 357 | } 358 | return h[0]; 359 | } 360 | 361 | // 获取子串哈希,左闭右开区间 362 | ULL get_substring_hash(int left, int right) const { 363 | int len = right - left; 364 | #ifdef ENABLE_DOUBLE_HASH 365 | // get hash of s[left...right-1] 366 | unsigned int mask32 = ~(0u); 367 | ULL left1 = h[left] >> 32, right1 = h[right] >> 32; 368 | ULL left2 = h[left] & mask32, right2 = h[right] & mask32; 369 | return (((left1 - right1 * xp1[len] % p1 + p1) % p1) << 32) | 370 | (((left2 - right2 * xp2[len] % p2 + p2) % p2)); 371 | #else 372 | return h[left] - h[right] * xp[len]; 373 | #endif 374 | } 375 | 376 | void get_all_subs_hash(int sublen) { 377 | subsize = length - sublen + 1; 378 | for (int i = 0; i < subsize; ++i) 379 | hl[i] = get_substring_hash(i, i + sublen); 380 | sorted = 0; 381 | } 382 | 383 | void sort_substring_hash() { 384 | sort(hl, hl + subsize); 385 | sorted = 1; 386 | } 387 | 388 | bool match(ULL key) const { 389 | if (!sorted) assert (0); 390 | if (!subsize) return false; 391 | return binary_search(hl, hl + subsize, key); 392 | } 393 | 394 | void init(const char *t) { 395 | length = strlen(t); 396 | strcpy(s, t); 397 | } 398 | }; 399 | 400 | int LCP(const String &a, const String &b, int ai, int bi) { 401 | // Find LCP of a[ai...] and b[bi...] 402 | int l = 0, r = min(a.length - ai, b.length - bi); 403 | while (l < r) { 404 | int mid = (l + r + 1) / 2; 405 | if (a.get_substring_hash(ai, ai + mid) == b.get_substring_hash(bi, bi + mid)) 406 | l = mid; 407 | else r = mid - 1; 408 | } 409 | return l; 410 | } 411 | 412 | int check(int ans) { 413 | if (T.length < ans) return 1; 414 | T.get_all_subs_hash(ans); T.sort_substring_hash(); 415 | for (int i = 0; i < S.length - ans + 1; ++i) 416 | if (!T.match(S.get_substring_hash(i, i + ans))) 417 | return 1; 418 | return 0; 419 | } 420 | 421 | int main() { 422 | init_xp(); // DON'T FORGET TO DO THIS! 423 | 424 | for (int tt = 1; tt <= kases; ++tt) { 425 | scanf("%d", &n); scanf("%s", str); 426 | S.init(str); 427 | S.hash(); T.hash(); 428 | } 429 | } 430 | ``` 431 | 432 | 二维哈希 433 | 434 | ```cpp 435 | struct Hash2D { // 1-index 436 | static const LL px = 131, py = 233, MOD = 998244353; 437 | static LL pwx[N], pwy[N]; 438 | int a[N][N]; 439 | LL hv[N][N]; 440 | static void init_xp() { 441 | pwx[0] = pwy[0] = 1; 442 | FOR (i, 1, N) { 443 | pwx[i] = pwx[i - 1] * px % MOD; 444 | pwy[i] = pwy[i - 1] * py % MOD; 445 | } 446 | } 447 | void init_hash(int n, int m) { 448 | FOR (i, 1, n + 1) { 449 | LL s = 0; 450 | FOR (j, 1, m + 1) { 451 | s = (s * py + a[i][j]) % MOD; 452 | hv[i][j] = (hv[i - 1][j] * px + s) % MOD; 453 | } 454 | } 455 | } 456 | LL h(int x, int y, int dx, int dy) { 457 | --x; --y; 458 | LL ret = hv[x + dx][y + dy] + hv[x][y] * pwx[dx] % MOD * pwy[dy] 459 | - hv[x][y + dy] * pwx[dx] - hv[x + dx][y] * pwy[dy]; 460 | return (ret % MOD + MOD) % MOD; 461 | } 462 | } ha, hb; 463 | LL Hash2D::pwx[N], Hash2D::pwy[N]; 464 | ``` 465 | 466 | ## 后缀数组 467 | 468 | 构造时间:$O(L \log L)$;查询时间 $O(\log L)$。`suffix` 数组是排好序的后缀下标, `suffix` 的反数组是后缀数组。 469 | 470 | ```cpp 471 | #include 472 | using namespace std; 473 | 474 | const int N = 2e5 + 10; 475 | const int Nlog = 18; 476 | 477 | struct SuffixArray { 478 | const int L; 479 | vector > P; 480 | vector, int> > M; 481 | int s[N], sa[N], rank[N], height[N]; 482 | // s: raw string 483 | // sa[i]=k: s[k...L-1] ranks i (0 based) 484 | // rank[i]=k: the rank of s[i...L-1] is k (0 based) 485 | // height[i] = lcp(sa[i-1], sa[i]) 486 | 487 | SuffixArray(const string &raw_s) : L(raw_s.length()), P(1, vector(L, 0)), M(L) { 488 | for (int i = 0; i < L; i++) 489 | P[0][i] = this->s[i] = int(raw_s[i]); 490 | for (int skip = 1, level = 1; skip < L; skip *= 2, level++) { 491 | P.push_back(vector(L, 0)); 492 | for (int i = 0; i < L; i++) 493 | M[i] = make_pair(make_pair(P[level - 1][i], i + skip < L ? P[level - 1][i + skip] : -1000), i); 494 | sort(M.begin(), M.end()); 495 | for (int i = 0; i < L; i++) 496 | P[level][M[i].second] = (i > 0 && M[i].first == M[i - 1].first) ? P[level][M[i - 1].second] : i; 497 | } 498 | for (unsigned i = 0; i < P.back().size(); ++i) { 499 | rank[i] = P.back()[i]; 500 | sa[rank[i]] = i; 501 | } 502 | } 503 | 504 | // This is a traditional way to calculate LCP 505 | void getHeight() { 506 | memset(height, 0, sizeof height); 507 | int k = 0; 508 | for (int i = 0; i < L; ++i) { 509 | if (rank[i] == 0) continue; 510 | if (k) k--; 511 | int j = sa[rank[i] - 1]; 512 | while (i + k < L && j + k < L && s[i + k] == s[j + k]) ++k; 513 | height[rank[i]] = k; 514 | } 515 | rmq_init(height, L); 516 | } 517 | 518 | int f[N][Nlog]; 519 | inline int highbit(int x) { 520 | return 31 - __builtin_clz(x); 521 | } 522 | 523 | int rmq_query(int x, int y) { 524 | int p = highbit(y - x + 1); 525 | return min(f[x][p], f[y - (1 << p) + 1][p]); 526 | } 527 | 528 | // arr has to be 0 based 529 | void rmq_init(int *arr, int length) { 530 | for (int x = 0; x <= highbit(length); ++x) 531 | for (int i = 0; i <= length - (1 << x); ++i) { 532 | if (!x) f[i][x] = arr[i]; 533 | else f[i][x] = min(f[i][x - 1], f[i + (1 << (x - 1))][x - 1]); 534 | } 535 | } 536 | 537 | #ifdef NEW 538 | // returns the length of the longest common prefix of s[i...L-1] and s[j...L-1] 539 | int LongestCommonPrefix(int i, int j) { 540 | int len = 0; 541 | if (i == j) return L - i; 542 | for (int k = (int) P.size() - 1; k >= 0 && i < L && j < L; k--) { 543 | if (P[k][i] == P[k][j]) { 544 | i += 1 << k; 545 | j += 1 << k; 546 | len += 1 << k; 547 | } 548 | } 549 | return len; 550 | } 551 | #else 552 | int LongestCommonPrefix(int i, int j) { 553 | // getHeight() must be called first 554 | if (i == j) return L - i; 555 | if (i > j) swap(i, j); 556 | return rmq_query(i + 1, j); 557 | } 558 | #endif 559 | 560 | int checkNonOverlappingSubstring(int K) { 561 | // check if there is two non-overlapping identical substring of length K 562 | int minsa = 0, maxsa = 0; 563 | for (int i = 0; i < L; ++i) { 564 | if (height[i] < K) { 565 | minsa = sa[i]; maxsa = sa[i]; 566 | } else { 567 | minsa = min(minsa, sa[i]); 568 | maxsa = max(maxsa, sa[i]); 569 | if (maxsa - minsa >= K) return 1; 570 | } 571 | } 572 | return 0; 573 | } 574 | 575 | int checkBelongToDifferentSubstring(int K, int split) { 576 | int minsa = 0, maxsa = 0; 577 | for (int i = 0; i < L; ++i) { 578 | if (height[i] < K) { 579 | minsa = sa[i]; maxsa = sa[i]; 580 | } else { 581 | minsa = min(minsa, sa[i]); 582 | maxsa = max(maxsa, sa[i]); 583 | if (maxsa > split && minsa < split) return 1; 584 | } 585 | } 586 | return 0; 587 | } 588 | 589 | } *S; 590 | 591 | int main() { 592 | string s, t; 593 | cin >> s >> t; 594 | int sp = s.length(); 595 | s += "*" + t; 596 | S = new SuffixArray(s); 597 | S->getHeight(); 598 | int left = 0, right = sp; 599 | while (left < right) { 600 | int mid = (left + right + 1) / 2; 601 | if (S->checkBelongToDifferentSubstring(mid, sp)) 602 | left = mid; 603 | else right = mid - 1; 604 | } 605 | printf("%d\n", left); 606 | } 607 | ``` 608 | 609 | + SA-IS 610 | + 仅在后缀自动机被卡内存或者卡常且需要 O(1) LCA 的情况下使用(比赛中敲这个我觉得不行) 611 | + UOJ 35 612 | 613 | ```cpp 614 | // rk [0..n-1] -> [1..n], sa/ht [1..n] 615 | // s[i] > 0 && s[n] = 0 616 | // b: normally as bucket 617 | // c: normally as bucket1 618 | // d: normally as bucket2 619 | // f: normally as cntbuf 620 | 621 | template 622 | struct SuffixArray { 623 | bool t[size << 1]; 624 | int b[size], c[size]; 625 | int sa[size], rk[size], ht[size]; 626 | inline bool isLMS(const int i, const bool *t) { return i > 0 && t[i] && !t[i - 1]; } 627 | template 628 | inline void inducedSort(T s, int *sa, const int n, const int M, const int bs, 629 | bool *t, int *b, int *f, int *p) { 630 | fill(b, b + M, 0); fill(sa, sa + n, -1); 631 | FOR (i, 0, n) b[s[i]]++; 632 | f[0] = b[0]; 633 | FOR (i, 1, M) f[i] = f[i - 1] + b[i]; 634 | FORD (i, bs - 1, -1) sa[--f[s[p[i]]]] = p[i]; 635 | FOR (i, 1, M) f[i] = f[i - 1] + b[i - 1]; 636 | FOR (i, 0, n) if (sa[i] > 0 && !t[sa[i] - 1]) sa[f[s[sa[i] - 1]]++] = sa[i] - 1; 637 | f[0] = b[0]; 638 | FOR (i, 1, M) f[i] = f[i - 1] + b[i]; 639 | FORD (i, n - 1, -1) if (sa[i] > 0 && t[sa[i] - 1]) sa[--f[s[sa[i] - 1]]] = sa[i] - 1; 640 | } 641 | template 642 | inline void sais(T s, int *sa, int n, bool *t, int *b, int *c, int M) { 643 | int i, j, bs = 0, cnt = 0, p = -1, x, *r = b + M; 644 | t[n - 1] = 1; 645 | FORD (i, n - 2, -1) t[i] = s[i] < s[i + 1] || (s[i] == s[i + 1] && t[i + 1]); 646 | FOR (i, 1, n) if (t[i] && !t[i - 1]) c[bs++] = i; 647 | inducedSort(s, sa, n, M, bs, t, b, r, c); 648 | for (i = bs = 0; i < n; i++) if (isLMS(sa[i], t)) sa[bs++] = sa[i]; 649 | FOR (i, bs, n) sa[i] = -1; 650 | FOR (i, 0, bs) { 651 | x = sa[i]; 652 | for (j = 0; j < n; j++) { 653 | if (p == -1 || s[x + j] != s[p + j] || t[x + j] != t[p + j]) { cnt++, p = x; break; } 654 | else if (j > 0 && (isLMS(x + j, t) || isLMS(p + j, t))) break; 655 | } 656 | x = (~x & 1 ? x >> 1 : x - 1 >> 1), sa[bs + x] = cnt - 1; 657 | } 658 | for (i = j = n - 1; i >= bs; i--) if (sa[i] >= 0) sa[j--] = sa[i]; 659 | int *s1 = sa + n - bs, *d = c + bs; 660 | if (cnt < bs) sais(s1, sa, bs, t + n, b, c + bs, cnt); 661 | else FOR (i, 0, bs) sa[s1[i]] = i; 662 | FOR (i, 0, bs) d[i] = c[sa[i]]; 663 | inducedSort(s, sa, n, M, bs, t, b, r, d); 664 | } 665 | template 666 | inline void getHeight(T s, const int n, const int *sa) { 667 | for (int i = 0, k = 0; i < n; i++) { 668 | if (rk[i] == 0) k = 0; 669 | else { 670 | if (k > 0) k--; 671 | int j = sa[rk[i] - 1]; 672 | while (i + k < n && j + k < n && s[i + k] == s[j + k]) k++; 673 | } 674 | ht[rk[i]] = k; 675 | } 676 | } 677 | template 678 | inline void init(T s, int n, int M) { 679 | sais(s, sa, ++n, t, b, c, M); 680 | for (int i = 1; i < n; i++) rk[sa[i]] = i; 681 | getHeight(s, n, sa); 682 | } 683 | }; 684 | 685 | const int N = 2E5 + 100; 686 | SuffixArray sa; 687 | 688 | int main() { 689 | string s; cin >> s; int n = s.length(); 690 | sa.init(s, n, 128); 691 | FOR (i, 1, n + 1) printf("%d%c", sa.sa[i] + 1, i == _i - 1 ? '\n' : ' '); 692 | FOR (i, 2, n + 1) printf("%d%c", sa.ht[i], i == _i - 1 ? '\n' : ' '); 693 | } 694 | ``` 695 | 696 | ## KMP 697 | 698 | + 前缀函数(每一个前缀的最长 border) 699 | 700 | ```cpp 701 | void get_pi(int a[], char s[], int n) { 702 | int j = a[0] = 0; 703 | FOR (i, 1, n) { 704 | while (j && s[i] != s[j]) j = a[j - 1]; 705 | a[i] = j += s[i] == s[j]; 706 | } 707 | } 708 | ``` 709 | 710 | + Z 函数(每一个后缀和该字符串的 LCP 长度) 711 | 712 | ```cpp 713 | void get_z(int a[], char s[], int n) { 714 | int l = 0, r = 0; a[0] = n; 715 | FOR (i, 1, n) { 716 | a[i] = i > r ? 0 : min(r - i + 1, a[i - l]); 717 | while (i + a[i] < n && s[a[i]] == s[i + a[i]]) ++a[i]; 718 | if (i + a[i] - 1 > r) { l = i; r = i + a[i] - 1; } 719 | } 720 | } 721 | ``` 722 | 723 | 724 | ## Trie 725 | 726 | ```cpp 727 | namespace trie { 728 | int t[N][26], sz, ed[N]; 729 | void init() { sz = 2; memset(ed, 0, sizeof ed); } 730 | int _new() { memset(t[sz], 0, sizeof t[sz]); return sz++; } 731 | void ins(char* s, int p) { 732 | int u = 1; 733 | FOR (i, 0, strlen(s)) { 734 | int c = s[i] - 'a'; 735 | if (!t[u][c]) t[u][c] = _new(); 736 | u = t[u][c]; 737 | } 738 | ed[u] = p; 739 | } 740 | } 741 | ``` 742 | 743 | ## AC 自动机 744 | 745 | ```cpp 746 | const int N = 1e6 + 100, M = 26; 747 | 748 | int mp(char ch) { return ch - 'a'; } 749 | 750 | struct ACA { 751 | int ch[N][M], danger[N], fail[N]; 752 | int sz; 753 | void init() { 754 | sz = 1; 755 | memset(ch[0], 0, sizeof ch[0]); 756 | memset(danger, 0, sizeof danger); 757 | } 758 | void insert(const string &s, int m) { 759 | int n = s.size(); int u = 0, c; 760 | FOR (i, 0, n) { 761 | c = mp(s[i]); 762 | if (!ch[u][c]) { 763 | memset(ch[sz], 0, sizeof ch[sz]); 764 | danger[sz] = 0; ch[u][c] = sz++; 765 | } 766 | u = ch[u][c]; 767 | } 768 | danger[u] |= 1 << m; 769 | } 770 | void build() { 771 | queue Q; 772 | fail[0] = 0; 773 | for (int c = 0, u; c < M; c++) { 774 | u = ch[0][c]; 775 | if (u) { Q.push(u); fail[u] = 0; } 776 | } 777 | while (!Q.empty()) { 778 | int r = Q.front(); Q.pop(); 779 | danger[r] |= danger[fail[r]]; 780 | for (int c = 0, u; c < M; c++) { 781 | u = ch[r][c]; 782 | if (!u) { 783 | ch[r][c] = ch[fail[r]][c]; 784 | continue; 785 | } 786 | fail[u] = ch[fail[r]][c]; 787 | Q.push(u); 788 | } 789 | } 790 | } 791 | } ac; 792 | 793 | char s[N]; 794 | 795 | int main() { 796 | int n; scanf("%d", &n); 797 | ac.init(); 798 | while (n--) { 799 | scanf("%s", s); 800 | ac.insert(s, 0); 801 | } 802 | ac.build(); 803 | 804 | scanf("%s", s); 805 | int u = 0; n = strlen(s); 806 | FOR (i, 0, n) { 807 | u = ac.ch[u][mp(s[i])]; 808 | if (ac.danger[u]) { 809 | puts("YES"); 810 | return 0; 811 | } 812 | } 813 | puts("NO"); 814 | return 0; 815 | } 816 | ``` 817 | 818 | -------------------------------------------------------------------------------- /6-杂项.md: -------------------------------------------------------------------------------- 1 | # 杂项 2 | 3 | ## STL 4 | 5 | + copy 6 | ```cpp 7 | template 8 | OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result); 9 | ``` 10 | + merge (如果相等,第一个优先) 11 | ```cpp 12 | template 14 | OutputIterator merge (InputIterator1 first1, InputIterator1 last1, 15 | InputIterator2 first2, InputIterator2 last2, 16 | OutputIterator result, Compare comp); 17 | ``` 18 | + for_each 19 | ```cpp 20 | template 21 | Function for_each (InputIterator first, InputIterator last, Function fn); 22 | ``` 23 | + transform 24 | ```cpp 25 | template 26 | OutputIterator transform (InputIterator first1, InputIterator last1, 27 | OutputIterator result, UnaryOperation op); 28 | ``` 29 | + numeric_limits 30 | ```cpp 31 | template numeric_limits; 32 | ``` 33 | + iota 34 | 35 | ```cpp 36 | template< class ForwardIterator, class T > 37 | void iota( ForwardIterator first, ForwardIterator last, T value ); 38 | ``` 39 | 40 | ## 日期 41 | 42 | ```cpp 43 | // Routines for performing computations on dates. In these routines, 44 | // months are exprsesed as integers from 1 to 12, days are expressed 45 | // as integers from 1 to 31, and years are expressed as 4-digit 46 | // integers. 47 | 48 | string dayOfWeek[] = {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}; 49 | 50 | // converts Gregorian date to integer (Julian day number) 51 | 52 | int DateToInt (int m, int d, int y){ 53 | return 54 | 1461 * (y + 4800 + (m - 14) / 12) / 4 + 55 | 367 * (m - 2 - (m - 14) / 12 * 12) / 12 - 56 | 3 * ((y + 4900 + (m - 14) / 12) / 100) / 4 + 57 | d - 32075; 58 | } 59 | 60 | // converts integer (Julian day number) to Gregorian date: month/day/year 61 | 62 | void IntToDate (int jd, int &m, int &d, int &y){ 63 | int x, n, i, j; 64 | 65 | x = jd + 68569; 66 | n = 4 * x / 146097; 67 | x -= (146097 * n + 3) / 4; 68 | i = (4000 * (x + 1)) / 1461001; 69 | x -= 1461 * i / 4 - 31; 70 | j = 80 * x / 2447; 71 | d = x - 2447 * j / 80; 72 | x = j / 11; 73 | m = j + 2 - 12 * x; 74 | y = 100 * (n - 49) + i + x; 75 | } 76 | 77 | // converts integer (Julian day number) to day of week 78 | 79 | string IntToDay (int jd){ 80 | return dayOfWeek[jd % 7]; 81 | } 82 | ``` 83 | 84 | ## 子集枚举 85 | 86 | + 枚举真子集 87 | 88 | ```cpp 89 | for (int s = (S - 1) & S; s; s = (s - 1) & S) 90 | ``` 91 | 92 | + 枚举大小为 k 的子集 93 | 94 | ```cpp 95 | template 96 | void subset(int k, int n, T&& f) { 97 | int t = (1 << k) - 1; 98 | while (t < 1 << n) { 99 | f(t); 100 | int x = t & -t, y = t + x; 101 | t = ((t & ~y) / x >> 1) | y; 102 | } 103 | } 104 | ``` 105 | 106 | ## 数位 DP 107 | 108 | ```cpp 109 | LL dfs(LL base, LL pos, LL len, LL s, bool limit) { 110 | if (pos == -1) return s ? base : 1; 111 | if (!limit && dp[base][pos][len][s] != -1) return dp[base][pos][len][s]; 112 | LL ret = 0; 113 | LL ed = limit ? a[pos] : base - 1; 114 | FOR (i, 0, ed + 1) { 115 | tmp[pos] = i; 116 | if (len == pos) 117 | ret += dfs(base, pos - 1, len - (i == 0), s, limit && i == a[pos]); 118 | else if (s &&pos < (len + 1) / 2) 119 | ret += dfs(base, pos - 1, len, tmp[len - pos] == i, limit && i == a[pos]); 120 | else 121 | ret += dfs(base, pos - 1, len, s, limit && i == a[pos]); 122 | } 123 | if (!limit) dp[base][pos][len][s] = ret; 124 | return ret; 125 | } 126 | 127 | LL solve(LL x, LL base) { 128 | LL sz = 0; 129 | while (x) { 130 | a[sz++] = x % base; 131 | x /= base; 132 | } 133 | return dfs(base, sz - 1, sz - 1, 1, true); 134 | } 135 | ``` 136 | 137 | ## 模拟退火 138 | 139 | + 最小覆盖圆 140 | 141 | ```cpp 142 | using LD = double; 143 | const int N = 1E4 + 100; 144 | int x[N], y[N], n; 145 | 146 | LD eval(LD xx, LD yy) { 147 | LD r = 0; 148 | FOR (i, 0, n) 149 | r = max(r, sqrt(pow(xx - x[i], 2) + pow(yy - y[i], 2))); 150 | return r; 151 | } 152 | 153 | mt19937 mt(time(0)); 154 | auto rd = bind(uniform_real_distribution(-1, 1), mt); 155 | 156 | 157 | int main() { 158 | int X, Y; 159 | while (cin >> X >> Y >> n) { 160 | FOR (i, 0, n) scanf("%d%d", &x[i], &y[i]); 161 | pair ans; 162 | LD M = 1e9; 163 | FOR (_, 0, 100) { 164 | LD cur_x = X / 2.0, cur_y = Y / 2.0, T = max(X, Y); 165 | while (T > 1e-3) { 166 | LD best_ans = eval(cur_x, cur_y); 167 | LD best_x = cur_x, best_y = cur_y; 168 | FOR (___, 0, 20) { 169 | LD nxt_x = cur_x + rd() * T, nxt_y = cur_y + rd() * T; 170 | LD nxt_ans = eval(nxt_x, nxt_y); 171 | if (nxt_ans < best_ans) { 172 | best_x = nxt_x; best_y = nxt_y; 173 | best_ans = nxt_ans; 174 | } 175 | } 176 | cur_x = best_x; cur_y = best_y; 177 | T *= .9; 178 | } 179 | if (eval(cur_x, cur_y) < M) { 180 | ans = {cur_x, cur_y}; M = eval(cur_x, cur_y); 181 | } 182 | } 183 | printf("(%.1f,%.1f).\n%.1f\n", ans.first, ans.second, eval(ans.first, ans.second)); 184 | } 185 | } 186 | ``` 187 | 188 | ## 土制 bitset 189 | 190 | + 可以用 `auto p = reinterpret_cast(&x);` (`p[0]` 的最低位就是 `bitset` 的最低位) 191 | 192 | ```cpp 193 | // M 要开大至少 1 个 64 194 | const int M = (1E4 + 200) / 64; 195 | typedef unsigned long long ULL; 196 | const ULL ONE = 1; 197 | 198 | struct Bitset { 199 | ULL a[M]; 200 | void go(int x) { 201 | int offset = x / 64; x %= 64; 202 | for (int i = offset, j = 0; i + 1 < M; ++i, ++j) { 203 | a[j] |= a[i] >> x; 204 | if (x) a[j] |= a[i + 1] << (64 - x); // 不能左移 64 位 205 | } 206 | } 207 | void init() { memset(a, 0, sizeof a); } 208 | void set(int x) { 209 | int offset = x / 64; x %= 64; 210 | a[offset] |= (ONE << x); 211 | } 212 | void prt() { 213 | FOR (i, 0, M) FOR (j, 0, 64) putchar((a[i] & (ONE << j)) ? '1' : '0'); 214 | puts(""); 215 | } 216 | int lowbit() { 217 | FOR (i, 0, M) if (a[i]) return i * 64 + __builtin_ctzll(a[i]); 218 | assert (0); 219 | } 220 | int highbit(int x) { 221 | // [0,x) 的最高位 222 | int offset = x / 64; x %= 64; 223 | FORD (i, offset, -1) { 224 | if (!a[i]) continue; 225 | if (i == offset) { 226 | FORD (j, x - 1, -1) if ((ONE << j) & a[i]) { return i * 64 + j; } 227 | } else return i * 64 + 63 - __builtin_clzll(a[i]); 228 | } 229 | assert (0); 230 | } 231 | }; 232 | ``` 233 | 234 | ## 随机 235 | 236 | + 不要使用 `rand()`。 237 | + `chrono::steady_clock::now().time_since_epoch().count()` 可用于计时。 238 | + 64 位可以使用 `mt19937_64`。 239 | 240 | ```cpp 241 | int main() { 242 | mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); 243 | vector permutation(N); 244 | 245 | for (int i = 0; i < N; i++) 246 | permutation[i] = i; 247 | shuffle(permutation.begin(), permutation.end(), rng); 248 | 249 | for (int i = 0; i < N; i++) 250 | permutation[i] = i; 251 | for (int i = 1; i < N; i++) 252 | swap(permutation[i], permutation[uniform_int_distribution(0, i)(rng)]); 253 | ``` 254 | 255 | ### 伪随机数 256 | 257 | ```cpp 258 | unsigned rnd() { 259 | static unsigned A = 1 << 16 | 3, B = 33333331, C = 2341; 260 | return C = A * C + B; 261 | } 262 | ``` 263 | 264 | ### 真实随机数 265 | 266 | ```cpp 267 | mt19937 mt(time(0)); 268 | auto rd = bind(uniform_real_distribution(0, 1), mt); 269 | auto rd2 = bind(uniform_int_distribution(1, 6), mt); 270 | ``` 271 | 272 | ### 随机素数表 273 | 274 | 42737, 46411, 50101, 52627, 54577, 191677, 194869, 210407, 221831, 241337, 578603, 625409, 713569, 788813, 862481, 2174729, 2326673, 2688877, 2779417, 3133583, 4489747, 6697841, 6791471, 6878533, 7883129, 9124553, 10415371, 11134633, 12214801, 15589333, 17148757, 17997457, 20278487, 27256133, 28678757, 38206199, 41337119, 47422547, 48543479, 52834961, 76993291, 85852231, 95217823, 108755593, 132972461, 171863609, 173629837, 176939899, 207808351, 227218703, 306112619, 311809637, 322711981, 330806107, 345593317, 345887293, 362838523, 373523729, 394207349, 409580177, 437359931, 483577261, 490845269, 512059357, 534387017, 698987533, 764016151, 906097321, 914067307, 954169327 275 | 276 | 1572869, 3145739, 6291469, 12582917, 25165843, 50331653 (适合哈希的素数) 277 | 278 | ```python 279 | from random import randint 280 | 281 | def is_prime(num, test_count): 282 | if num == 1: 283 | return False 284 | if test_count >= num: 285 | test_count = num - 1 286 | for x in range(test_count): 287 | val = randint(1, num - 1) 288 | if pow(val, num-1, num) != 1: 289 | return False 290 | return True 291 | 292 | def generate_big_prime(n): 293 | found_prime = False 294 | while not found_prime: 295 | p = randint(2**(n-1), 2**n) 296 | if is_prime(p, 1000): 297 | return p 298 | ``` 299 | 300 | #### NTT 素数表 301 | 302 | $p= r2^k+1$,原根是 $g$。 303 | 304 | 3, 1, 1, 2; 5, 1, 2, 2; 17, 1, 4, 3; 97, 3, 5, 5; 193, 3, 6, 5; 257, 1, 8, 3; 7681, 15, 9, 17; 12289, 3, 12, 11; 40961, 5, 13, 3; 65537, 1, 16, 3; 786433, 3, 18, 10; 5767169, 11, 19, 3; 7340033, 7, 20, 3; 23068673, 11, 21, 3; 104857601, 25, 22, 3; 167772161, 5, 25, 3; 469762049, 7, 26, 3; 1004535809, 479, 21, 3; 2013265921, 15, 27, 31; 2281701377, 17, 27, 3; 3221225473, 3, 30, 5; 75161927681, 35, 31, 3; 77309411329, 9, 33, 7; 206158430209, 3, 36, 22; 2061584302081, 15, 37, 7; 2748779069441, 5, 39, 3; 6597069766657, 3, 41, 5; 39582418599937, 9, 42, 5; 79164837199873, 9, 43, 5; 263882790666241, 15, 44, 7; 1231453023109121, 35, 45, 3; 1337006139375617, 19, 46, 3; 3799912185593857, 27, 47, 5; 4222124650659841, 15, 48, 19; 7881299347898369, 7, 50, 6; 31525197391593473, 7, 52, 3; 180143985094819841, 5, 55, 6; 1945555039024054273, 27, 56, 5; 4179340454199820289, 29, 57, 3. 305 | 306 | ## Java 307 | 308 | ### Regex 309 | 310 | ```java 311 | // Code which demonstrates the use of Java's regular expression libraries. 312 | // This is a solution for 313 | // 314 | // Loglan: a logical language 315 | // http://acm.uva.es/p/v1/134.html 316 | 317 | import java.util.*; 318 | import java.util.regex.*; 319 | 320 | public class LogLan { 321 | 322 | public static void main(String args[]) { 323 | 324 | String regex = BuildRegex(); 325 | Pattern pattern = Pattern.compile(regex); 326 | 327 | Scanner s = new Scanner(System.in); 328 | while (true) { 329 | 330 | // In this problem, each sentence consists of multiple lines, where the last 331 | // line is terminated by a period. The code below reads lines until 332 | // encountering a line whose final character is a '.'. Note the use of 333 | // 334 | // s.length() to get length of string 335 | // s.charAt() to extract characters from a Java string 336 | // s.trim() to remove whitespace from the beginning and end of Java string 337 | // 338 | // Other useful String manipulation methods include 339 | // 340 | // s.compareTo(t) < 0 if s < t, lexicographically 341 | // s.indexOf("apple") returns index of first occurrence of "apple" in s 342 | // s.lastIndexOf("apple") returns index of last occurrence of "apple" in s 343 | // s.replace(c,d) replaces occurrences of character c with d 344 | // s.startsWith("apple) returns (s.indexOf("apple") == 0) 345 | // s.toLowerCase() / s.toUpperCase() returns a new lower/uppercased string 346 | // 347 | // Integer.parseInt(s) converts s to an integer (32-bit) 348 | // Long.parseLong(s) converts s to a long (64-bit) 349 | // Double.parseDouble(s) converts s to a double 350 | 351 | String sentence = ""; 352 | while (true) { 353 | sentence = (sentence + " " + s.nextLine()).trim(); 354 | if (sentence.equals("#")) return; 355 | if (sentence.charAt(sentence.length() - 1) == '.') break; 356 | } 357 | 358 | // now, we remove the period, and match the regular expression 359 | 360 | String removed_period = sentence.substring(0, sentence.length() - 1).trim(); 361 | if (pattern.matcher(removed_period).find()) { 362 | System.out.println("Good"); 363 | } else { 364 | System.out.println("Bad!"); 365 | } 366 | } 367 | } 368 | } 369 | ``` 370 | 371 | ### Decimal Format 372 | 373 | ```java 374 | // examples for printing floating point numbers 375 | 376 | import java.util.*; 377 | import java.io.*; 378 | import java.text.DecimalFormat; 379 | 380 | public class DecFormat { 381 | public static void main(String[] args) { 382 | DecimalFormat fmt; 383 | 384 | // round to at most 2 digits, leave of digits if not needed 385 | fmt = new DecimalFormat("#.##"); 386 | System.out.println(fmt.format(12345.6789)); // produces 12345.68 387 | System.out.println(fmt.format(12345.0)); // produces 12345 388 | System.out.println(fmt.format(0.0)); // produces 0 389 | System.out.println(fmt.format(0.01)); // produces .1 390 | 391 | // round to precisely 2 digits 392 | fmt = new DecimalFormat("#.00"); 393 | System.out.println(fmt.format(12345.6789)); // produces 12345.68 394 | System.out.println(fmt.format(12345.0)); // produces 12345.00 395 | System.out.println(fmt.format(0.0)); // produces .00 396 | 397 | // round to precisely 2 digits, force leading zero 398 | fmt = new DecimalFormat("0.00"); 399 | System.out.println(fmt.format(12345.6789)); // produces 12345.68 400 | System.out.println(fmt.format(12345.0)); // produces 12345.00 401 | System.out.println(fmt.format(0.0)); // produces 0.00 402 | 403 | // round to precisely 2 digits, force leading zeros 404 | fmt = new DecimalFormat("000000000.00"); 405 | System.out.println(fmt.format(12345.6789)); // produces 000012345.68 406 | System.out.println(fmt.format(12345.0)); // produces 000012345.00 407 | System.out.println(fmt.format(0.0)); // produces 000000000.00 408 | 409 | // force leading '+' 410 | fmt = new DecimalFormat("+0;-0"); 411 | System.out.println(fmt.format(12345.6789)); // produces +12346 412 | System.out.println(fmt.format(-12345.6789)); // produces -12346 413 | System.out.println(fmt.format(0)); // produces +0 414 | 415 | // force leading positive/negative, pad to 2 416 | fmt = new DecimalFormat("positive 00;negative 0"); 417 | System.out.println(fmt.format(1)); // produces "positive 01" 418 | System.out.println(fmt.format(-1)); // produces "negative 01" 419 | 420 | // qoute special chars (#) 421 | fmt = new DecimalFormat("text with '#' followed by #"); 422 | System.out.println(fmt.format(12.34)); // produces "text with # followed by 12" 423 | 424 | // always show "." 425 | fmt = new DecimalFormat("#.#"); 426 | fmt.setDecimalSeparatorAlwaysShown(true); 427 | System.out.println(fmt.format(12.34)); // produces "12.3" 428 | System.out.println(fmt.format(12)); // produces "12." 429 | System.out.println(fmt.format(0.34)); // produces "0.3" 430 | 431 | // different grouping distances: 432 | fmt = new DecimalFormat("#,####.###"); 433 | System.out.println(fmt.format(123456789.123)); // produces "1,2345,6789.123" 434 | 435 | // scientific: 436 | fmt = new DecimalFormat("0.000E00"); 437 | System.out.println(fmt.format(123456789.123)); // produces "1.235E08" 438 | System.out.println(fmt.format(-0.000234)); // produces "-2.34E-04" 439 | 440 | // using variable number of digits: 441 | fmt = new DecimalFormat("0"); 442 | System.out.println(fmt.format(123.123)); // produces "123" 443 | fmt.setMinimumFractionDigits(8); 444 | System.out.println(fmt.format(123.123)); // produces "123.12300000" 445 | fmt.setMaximumFractionDigits(0); 446 | System.out.println(fmt.format(123.123)); // produces "123" 447 | 448 | // note: to pad with spaces, you need to do it yourself: 449 | // String out = fmt.format(...) 450 | // while (out.length() < targlength) out = " "+out; 451 | } 452 | } 453 | ``` 454 | 455 | ### Sort 456 | 457 | ```java 458 | import java.util.ArrayList; 459 | import java.util.Collections; 460 | import java.util.List; 461 | 462 | public class Employee implements Comparable { 463 | private int id; 464 | private String name; 465 | private int age; 466 | 467 | public Employee(int id, String name, int age) { 468 | this.id = id; 469 | this.name = name; 470 | this.age = age; 471 | } 472 | 473 | @Override 474 | public int compareTo(Employee o) { 475 | if (id > o.id) { 476 | return 1; 477 | } else if (id < o.id) { 478 | return -1; 479 | } 480 | return 0; 481 | } 482 | 483 | public static void main(String[] args) { 484 | List list = new ArrayList(); 485 | list.add(new Employee(2, "Java", 20)); 486 | list.add(new Employee(1, "C", 30)); 487 | list.add(new Employee(3, "C#", 10)); 488 | Collections.sort(list); 489 | } 490 | } 491 | ``` 492 | 493 | 494 | ## 扩栈(本地使用) 495 | 496 | ```cpp 497 | #include 498 | void init_stack(){ 499 | const rlim_t kStackSize = 512 * 1024 * 1024; 500 | struct rlimit rl; 501 | int result; 502 | result = getrlimit(RLIMIT_STACK, &rl); 503 | if (result == 0) { 504 | if (rl.rlim_cur < kStackSize) { 505 | rl.rlim_cur = kStackSize; 506 | result = setrlimit(RLIMIT_STACK, &rl); 507 | if (result != 0) { 508 | fprintf(stderr, "setrlimit returned result = %d\n", result); 509 | } 510 | } 511 | } 512 | } 513 | ``` 514 | 515 | ## 心态崩了 516 | 517 | + `(int)v.size()` 518 | + `1LL << k` 519 | + 递归函数用全局或者 static 变量要小心 520 | + 预处理组合数注意上限 521 | + 想清楚到底是要 `multiset` 还是 `set` 522 | + 提交之前看一下数据范围,测一下边界 523 | + 数据结构注意数组大小 (2倍,4倍) 524 | + 字符串注意字符集 525 | + 如果函数中使用了默认参数的话,注意调用时的参数个数。 526 | + 注意要读完 527 | + 构造参数无法使用自己 528 | + 树链剖分/dfs 序,初始化或者询问不要忘记 idx, ridx 529 | + 排序时注意结构体的所有属性是不是考虑了 530 | + 不要把 while 写成 if 531 | + 不要把 int 开成 char 532 | + 清零的时候全部用 0~n+1。 533 | + 模意义下不要用除法 534 | + 哈希不要自然溢出 535 | + 最短路不要 SPFA,乖乖写 Dijkstra 536 | + 上取整以及 GCD 小心负数 537 | + mid 用 `l + (r - l) / 2` 可以避免溢出和负数的问题 538 | + 小心模板自带的意料之外的隐式类型转换 539 | + 求最优解时不要忘记更新当前最优解 540 | + 图论问题一定要注意图不连通的问题 541 | + 处理强制在线的时候 lastans 负数也要记得矫正 542 | + 不要觉得编译器什么都能优化 543 | + 分块一定要特判在同一块中的情况 544 | -------------------------------------------------------------------------------- /assets/sam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/template/15da4957941bfe29db1dc23c0a16d1a34e53e3e6/assets/sam.png -------------------------------------------------------------------------------- /pandoc/algo.latex: -------------------------------------------------------------------------------- 1 | \PassOptionsToPackage{unicode=true}{hyperref} % options for packages loaded elsewhere 2 | \PassOptionsToPackage{hyphens}{url} 3 | $if(colorlinks)$ 4 | \PassOptionsToPackage{dvipsnames,svgnames*,x11names*}{xcolor} 5 | $endif$ 6 | % 7 | \documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$babel-lang$,$endif$$if(papersize)$$papersize$paper,$endif$$if(beamer)$ignorenonframetext,$if(handout)$handout,$endif$$if(aspectratio)$aspectratio=$aspectratio$,$endif$$endif$$for(classoption)$$classoption$$sep$,$endfor$]{$documentclass$} 8 | $if(beamer)$ 9 | \setbeamertemplate{caption}[numbered] 10 | \setbeamertemplate{caption label separator}{: } 11 | \setbeamercolor{caption name}{fg=normal text.fg} 12 | \beamertemplatenavigationsymbols$if(navigation)$$navigation$$else$empty$endif$ 13 | $endif$ 14 | $if(beamerarticle)$ 15 | \usepackage{beamerarticle} % needs to be loaded first 16 | $endif$ 17 | $if(fontfamily)$ 18 | \usepackage[$for(fontfamilyoptions)$$fontfamilyoptions$$sep$,$endfor$]{$fontfamily$} 19 | $else$ 20 | \usepackage{lmodern} 21 | $endif$ 22 | $if(linestretch)$ 23 | \usepackage{setspace} 24 | \setstretch{$linestretch$} 25 | $endif$ 26 | \usepackage{amssymb,amsmath} 27 | 28 | % settings 29 | \usepackage{minted} 30 | % 31 | 32 | 33 | 34 | \usepackage{ifxetex,ifluatex} 35 | \usepackage{fixltx2e} % provides \textsubscript 36 | \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex 37 | \usepackage[$if(fontenc)$$fontenc$$else$T1$endif$]{fontenc} 38 | \usepackage[utf8]{inputenc} 39 | \usepackage{textcomp} % provides euro and other symbols 40 | \else % if luatex or xelatex 41 | $if(mathspec)$ 42 | \ifxetex 43 | \usepackage{mathspec} 44 | \else 45 | \usepackage{unicode-math} 46 | \fi 47 | $else$ 48 | \usepackage{unicode-math} 49 | $endif$ 50 | \defaultfontfeatures{Ligatures=TeX,Scale=MatchLowercase} 51 | $for(fontfamilies)$ 52 | \newfontfamily{$fontfamilies.name$}[$fontfamilies.options$]{$fontfamilies.font$} 53 | $endfor$ 54 | $if(mainfont)$ 55 | \setmainfont[$for(mainfontoptions)$$mainfontoptions$$sep$,$endfor$]{$mainfont$} 56 | $endif$ 57 | $if(sansfont)$ 58 | \setsansfont[$for(sansfontoptions)$$sansfontoptions$$sep$,$endfor$]{$sansfont$} 59 | $endif$ 60 | $if(monofont)$ 61 | \setmonofont[Mapping=tex-ansi$if(monofontoptions)$,$for(monofontoptions)$$monofontoptions$$sep$,$endfor$$endif$]{$monofont$} 62 | $endif$ 63 | $if(mathfont)$ 64 | $if(mathspec)$ 65 | \ifxetex 66 | \setmathfont(Digits,Latin,Greek)[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$} 67 | \else 68 | \setmathfont[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$} 69 | \fi 70 | $else$ 71 | \setmathfont[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$} 72 | $endif$ 73 | $endif$ 74 | $if(CJKmainfont)$ 75 | \ifxetex 76 | \usepackage{xeCJK} 77 | \setCJKmainfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$} 78 | \fi 79 | $endif$ 80 | $if(luatexjapresetoptions)$ 81 | \ifluatex 82 | \usepackage[$for(luatexjapresetoptions)$$luatexjapresetoptions$$sep$,$endfor$]{luatexja-preset} 83 | \fi 84 | $endif$ 85 | $if(CJKmainfont)$ 86 | \ifluatex 87 | \usepackage[$for(luatexjafontspecoptions)$$luatexjafontspecoptions$$sep$,$endfor$]{luatexja-fontspec} 88 | \setmainjfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$} 89 | \fi 90 | $endif$ 91 | \fi 92 | $if(beamer)$ 93 | $if(theme)$ 94 | \usetheme[$for(themeoptions)$$themeoptions$$sep$,$endfor$]{$theme$} 95 | $endif$ 96 | $if(colortheme)$ 97 | \usecolortheme{$colortheme$} 98 | $endif$ 99 | $if(fonttheme)$ 100 | \usefonttheme{$fonttheme$} 101 | $endif$ 102 | $if(mainfont)$ 103 | \usefonttheme{serif} % use mainfont rather than sansfont for slide text 104 | $endif$ 105 | $if(innertheme)$ 106 | \useinnertheme{$innertheme$} 107 | $endif$ 108 | $if(outertheme)$ 109 | \useoutertheme{$outertheme$} 110 | $endif$ 111 | $endif$ 112 | % use upquote if available, for straight quotes in verbatim environments 113 | \IfFileExists{upquote.sty}{\usepackage{upquote}}{} 114 | % use microtype if available 115 | \IfFileExists{microtype.sty}{% 116 | \usepackage[$for(microtypeoptions)$$microtypeoptions$$sep$,$endfor$]{microtype} 117 | \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts 118 | }{} 119 | $if(indent)$ 120 | $else$ 121 | \IfFileExists{parskip.sty}{% 122 | \usepackage{parskip} 123 | }{% else 124 | \setlength{\parindent}{0pt} 125 | \setlength{\parskip}{6pt plus 2pt minus 1pt} 126 | } 127 | $endif$ 128 | $if(verbatim-in-note)$ 129 | \usepackage{fancyvrb} 130 | $endif$ 131 | $if(colorlinks)$ 132 | \usepackage{xcolor} 133 | $endif$ 134 | \usepackage{hyperref} 135 | \hypersetup{ 136 | $if(title-meta)$ 137 | pdftitle={$title-meta$}, 138 | $endif$ 139 | $if(author-meta)$ 140 | pdfauthor={$author-meta$}, 141 | $endif$ 142 | $if(keywords)$ 143 | pdfkeywords={$for(keywords)$$keywords$$sep$, $endfor$}, 144 | $endif$ 145 | $if(colorlinks)$ 146 | colorlinks=true, 147 | linkcolor=$if(linkcolor)$$linkcolor$$else$Maroon$endif$, 148 | citecolor=$if(citecolor)$$citecolor$$else$Blue$endif$, 149 | urlcolor=$if(urlcolor)$$urlcolor$$else$Blue$endif$, 150 | $else$ 151 | pdfborder={0 0 0}, 152 | $endif$ 153 | breaklinks=true} 154 | \urlstyle{same} % don't use monospace font for urls 155 | $if(verbatim-in-note)$ 156 | \VerbatimFootnotes % allows verbatim text in footnotes 157 | $endif$ 158 | $if(geometry)$ 159 | \usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry} 160 | $endif$ 161 | $if(beamer)$ 162 | \newif\ifbibliography 163 | $endif$ 164 | $if(listings)$ 165 | \usepackage{listings} 166 | \newcommand{\passthrough}[1]{#1} 167 | \lstset{ 168 | basicstyle=\footnotesize\ttfamily, 169 | columns=flexible, 170 | breaklines=true, 171 | breakatwhitespace=false, 172 | numbers=left, 173 | numberstyle=\scriptsize, 174 | prebreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\rhookswarrow}}, 175 | postbreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\rcurvearrowse\space}}, 176 | } 177 | \usepackage{color} 178 | \definecolor{mygreen}{rgb}{0,0.6,0} 179 | \definecolor{mygray}{rgb}{0.5,0.5,0.5} 180 | \definecolor{mymauve}{rgb}{0.58,0,0.82} 181 | \lstset{ 182 | backgroundcolor=\color{white}, % choose the background color; you must add \usepackage{color} or \usepackage{xcolor}; should come as last argument 183 | % basicstyle=\footnotesize, % the size of the fonts that are used for the code 184 | % breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace 185 | % breaklines=true, % sets automatic line breaking 186 | % captionpos=b, % sets the caption-position to bottom 187 | commentstyle=\color{mygreen}, % comment style 188 | % deletekeywords={...}, % if you want to delete keywords from the given language 189 | % escapeinside={\%*}{*)}, % if you want to add LaTeX within your code 190 | % extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8 191 | % frame=single, % adds a frame around the code 192 | % keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible) 193 | keywordstyle=\color{blue}, % keyword style 194 | % language=Octave, % the language of the code 195 | morekeywords={*,...}, % if you want to add more keywords to the set 196 | % numbers=left, % where to put the line-numbers; possible values are (none, left, right) 197 | % numbersep=5pt, % how far the line-numbers are from the code 198 | % numberstyle=\tiny\color{mygray}, % the style that is used for the line-numbers 199 | rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here)) 200 | showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces' 201 | showstringspaces=false, % underline spaces within strings only 202 | showtabs=false, % show tabs within strings adding particular underscores 203 | % stepnumber=2, % the step between two line-numbers. If it's 1, each line will be numbered 204 | stringstyle=\color{mymauve}, % string literal style 205 | % tabsize=2, % sets default tabsize to 2 spaces 206 | % title=\lstname % show the filename of files included with \lstinputlisting; also try caption instead of title 207 | } 208 | $endif$ 209 | $if(lhs)$ 210 | \lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily,}}{} 211 | $endif$ 212 | $if(highlighting-macros)$ 213 | $highlighting-macros$ 214 | $endif$ 215 | $if(tables)$ 216 | \usepackage{longtable,booktabs} 217 | $if(beamer)$ 218 | \usepackage{caption} 219 | % These lines are needed to make table captions work with longtable: 220 | \makeatletter 221 | \def\fnum@table{\tablename~\thetable} 222 | \makeatother 223 | $else$ 224 | % Fix footnotes in tables (requires footnote package) 225 | \IfFileExists{footnote.sty}{\usepackage{footnote}\makesavenoteenv{longtable}}{} 226 | $endif$ 227 | $endif$ 228 | $if(graphics)$ 229 | \usepackage{graphicx,grffile} 230 | \makeatletter 231 | \def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi} 232 | \def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi} 233 | \makeatother 234 | % Scale images if necessary, so that they will not overflow the page 235 | % margins by default, and it is still possible to overwrite the defaults 236 | % using explicit options in \includegraphics[width, height, ...]{} 237 | \setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio} 238 | $endif$ 239 | $if(beamer)$ 240 | % Prevent slide breaks in the middle of a paragraph: 241 | \widowpenalties 1 10000 242 | \raggedbottom 243 | $if(section-titles)$ 244 | \AtBeginPart{ 245 | \let\insertpartnumber\relax 246 | \let\partname\relax 247 | \frame{\partpage} 248 | } 249 | \AtBeginSection{ 250 | \ifbibliography 251 | \else 252 | \let\insertsectionnumber\relax 253 | \let\sectionname\relax 254 | \frame{\sectionpage} 255 | \fi 256 | } 257 | \AtBeginSubsection{ 258 | \let\insertsubsectionnumber\relax 259 | \let\subsectionname\relax 260 | \frame{\subsectionpage} 261 | } 262 | $endif$ 263 | $endif$ 264 | $if(links-as-notes)$ 265 | % Make links footnotes instead of hotlinks: 266 | \DeclareRobustCommand{\href}[2]{#2\footnote{\url{#1}}} 267 | $endif$ 268 | $if(strikeout)$ 269 | \usepackage[normalem]{ulem} 270 | % avoid problems with \sout in headers with hyperref: 271 | \pdfstringdefDisableCommands{\renewcommand{\sout}{}} 272 | $endif$ 273 | \setlength{\emergencystretch}{3em} % prevent overfull lines 274 | \providecommand{\tightlist}{% 275 | \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} 276 | $if(numbersections)$ 277 | \setcounter{secnumdepth}{$if(secnumdepth)$$secnumdepth$$else$5$endif$} 278 | $else$ 279 | \setcounter{secnumdepth}{0} 280 | $endif$ 281 | $if(beamer)$ 282 | $else$ 283 | $if(subparagraph)$ 284 | $else$ 285 | % Redefines (sub)paragraphs to behave more like sections 286 | \ifx\paragraph\undefined\else 287 | \let\oldparagraph\paragraph 288 | \renewcommand{\paragraph}[1]{\oldparagraph{#1}\mbox{}} 289 | \fi 290 | \ifx\subparagraph\undefined\else 291 | \let\oldsubparagraph\subparagraph 292 | \renewcommand{\subparagraph}[1]{\oldsubparagraph{#1}\mbox{}} 293 | \fi 294 | $endif$ 295 | $endif$ 296 | 297 | 298 | % set default figure placement to htbp 299 | \makeatletter 300 | \def\fps@figure{htbp} 301 | \makeatother 302 | 303 | $for(header-includes)$ 304 | $header-includes$ 305 | $endfor$ 306 | 307 | $if(pagestyle)$ 308 | \pagestyle{$pagestyle$} 309 | $endif$ 310 | 311 | $if(lang)$ 312 | \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex 313 | \usepackage[shorthands=off,$for(babel-otherlangs)$$babel-otherlangs$,$endfor$main=$babel-lang$]{babel} 314 | $if(babel-newcommands)$ 315 | $babel-newcommands$ 316 | $endif$ 317 | \else 318 | % load polyglossia as late as possible as it *could* call bidi if RTL lang (e.g. Hebrew or Arabic) 319 | \usepackage{polyglossia} 320 | \setmainlanguage[$polyglossia-lang.options$]{$polyglossia-lang.name$} 321 | $for(polyglossia-otherlangs)$ 322 | \setotherlanguage[$polyglossia-otherlangs.options$]{$polyglossia-otherlangs.name$} 323 | $endfor$ 324 | \fi 325 | $endif$ 326 | $if(dir)$ 327 | \ifxetex 328 | % load bidi as late as possible as it modifies e.g. graphicx 329 | $if(latex-dir-rtl)$ 330 | \usepackage[RTLdocument]{bidi} 331 | $else$ 332 | \usepackage{bidi} 333 | $endif$ 334 | \fi 335 | \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex 336 | \TeXXeTstate=1 337 | \newcommand{\RL}[1]{\beginR #1\endR} 338 | \newcommand{\LR}[1]{\beginL #1\endL} 339 | \newenvironment{RTL}{\beginR}{\endR} 340 | \newenvironment{LTR}{\beginL}{\endL} 341 | \fi 342 | $endif$ 343 | $if(natbib)$ 344 | \usepackage[$natbiboptions$]{natbib} 345 | \bibliographystyle{$if(biblio-style)$$biblio-style$$else$plainnat$endif$} 346 | $endif$ 347 | $if(biblatex)$ 348 | \usepackage[$if(biblio-style)$style=$biblio-style$,$endif$$for(biblatexoptions)$$biblatexoptions$$sep$,$endfor$]{biblatex} 349 | $for(bibliography)$ 350 | \addbibresource{$bibliography$} 351 | $endfor$ 352 | $endif$ 353 | 354 | $if(title)$ 355 | \title{$title$$if(thanks)$\thanks{$thanks$}$endif$} 356 | $endif$ 357 | $if(subtitle)$ 358 | \providecommand{\subtitle}[1]{} 359 | \subtitle{$subtitle$} 360 | $endif$ 361 | $if(author)$ 362 | \author{$for(author)$$author$$sep$ \and $endfor$} 363 | $endif$ 364 | $if(institute)$ 365 | \providecommand{\institute}[1]{} 366 | \institute{$for(institute)$$institute$$sep$ \and $endfor$} 367 | $endif$ 368 | \date{$date$} 369 | $if(beamer)$ 370 | $if(titlegraphic)$ 371 | \titlegraphic{\includegraphics{$titlegraphic$}} 372 | $endif$ 373 | $if(logo)$ 374 | \logo{\includegraphics{$logo$}} 375 | $endif$ 376 | $endif$ 377 | 378 | \title{\vspace{50mm} \huge Standard Code Library \\[20pt]} 379 | \author{F0RE1GNERS \\[10pt] East China Normal University} 380 | \date{September 9102} 381 | 382 | 383 | \begin{document} 384 | $if(title)$ 385 | $if(beamer)$ 386 | \frame{\titlepage} 387 | $else$ 388 | \maketitle 389 | $endif$ 390 | $if(abstract)$ 391 | \begin{abstract} 392 | $abstract$ 393 | \end{abstract} 394 | $endif$ 395 | $endif$ 396 | 397 | \begin{titlepage} 398 | 399 | \maketitle 400 | 401 | \end{titlepage} 402 | 403 | \newpage 404 | 405 | $for(include-before)$ 406 | $include-before$ 407 | 408 | $endfor$ 409 | $if(toc)$ 410 | $if(beamer)$ 411 | \begin{frame} 412 | \tableofcontents[hideallsubsections] 413 | \end{frame} 414 | $else$ 415 | { 416 | $if(colorlinks)$ 417 | \hypersetup{linkcolor=$if(toccolor)$$toccolor$$else$$endif$} 418 | $endif$ 419 | \setcounter{tocdepth}{$toc-depth$} 420 | \tableofcontents 421 | \newpage 422 | } 423 | $endif$ 424 | $endif$ 425 | $if(lot)$ 426 | \listoftables 427 | $endif$ 428 | $if(lof)$ 429 | \listoffigures 430 | $endif$ 431 | 432 | $if(mytitle)$ 433 | \begin{center} 434 | \LARGE{\textbf{$mytitle$}} 435 | \end{center} 436 | $endif$ 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | $body$ 445 | 446 | $if(natbib)$ 447 | $if(bibliography)$ 448 | $if(biblio-title)$ 449 | $if(book-class)$ 450 | \renewcommand\bibname{$biblio-title$} 451 | $else$ 452 | \renewcommand\refname{$biblio-title$} 453 | $endif$ 454 | $endif$ 455 | $if(beamer)$ 456 | \begin{frame}[allowframebreaks]{$biblio-title$} 457 | \bibliographytrue 458 | $endif$ 459 | \bibliography{$for(bibliography)$$bibliography$$sep$,$endfor$} 460 | $if(beamer)$ 461 | \end{frame} 462 | $endif$ 463 | 464 | $endif$ 465 | $endif$ 466 | $if(biblatex)$ 467 | $if(beamer)$ 468 | \begin{frame}[allowframebreaks]{$biblio-title$} 469 | \bibliographytrue 470 | \printbibliography[heading=none] 471 | \end{frame} 472 | $else$ 473 | \printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$ 474 | $endif$ 475 | 476 | $endif$ 477 | $for(include-after)$ 478 | $include-after$ 479 | 480 | $endfor$ 481 | \end{document} 482 | -------------------------------------------------------------------------------- /pandoc/gen.sh: -------------------------------------------------------------------------------- 1 | pandoc --template algo --filter ./pandoc/minted.py --pdf-engine=xelatex --no-highlight --pdf-engine-opt="-shell-escape" -o template.tex --from markdown -V mainfont="Source Han Serif CN" -V monofont="Source Code Pro" -V sansfont="Source Han Sans CN" -V CJKmainfont="Source Han Serif CN" -V secnumdepth=2 -V --number-sections --toc -V include-before="\renewcommand\labelitemi{$\bullet$}" -V header-includes="\usepackage{minted}" -V geometry="margin=2cm" *-*.md 2 | latexmk -xelatex -shell-escape template.tex 3 | latexmk -c 4 | -------------------------------------------------------------------------------- /pandoc/minted.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | ''' 3 | Filter to wrap Pandoc's CodeBlocks into minted blocks when using latex. 4 | Pandoc's `fence_code_attributes` can be used to provide: 5 | - the language (first class) 6 | - minted's argumentless options (following classes) 7 | - minted's options with arguments (attributes) 8 | ''' 9 | 10 | from pandocfilters import toJSONFilter, RawBlock 11 | 12 | 13 | TEMPLATE = r''' 14 | \begin{{minted}}[fontsize=\footnotesize,breaklines,linenos{options}]{{{lang}}} 15 | {cont} 16 | \end{{minted}} 17 | '''.strip() 18 | 19 | 20 | def latex(x): 21 | return RawBlock('latex', x) 22 | 23 | 24 | def join_options(opts): 25 | return ',\n'.join(opts) 26 | 27 | 28 | def process_atts(kws): 29 | '''Preprocess the attributes provided by pandoc - they come as a list of 30 | 2-lists, convert to a list of strings''' 31 | return ['%s=%s' % (l, r) for l, r in kws] 32 | 33 | 34 | def mintedify(key, value, format_, meta): 35 | if key == 'CodeBlock': 36 | (ident, classes, attributes), contents = value 37 | if format_ == 'latex' and classes: 38 | language, *pos = classes 39 | atts = process_atts(attributes) 40 | return [latex(TEMPLATE.format(lang=language, 41 | options=join_options(pos+atts), 42 | cont=contents))] 43 | 44 | if __name__ == '__main__': 45 | toJSONFilter(mintedify) 46 | -------------------------------------------------------------------------------- /pandoc/readme.md: -------------------------------------------------------------------------------- 1 | Pandoc 生成 PDF 流程(以 Linux 为例): 2 | 3 | + 确保 `algo.latex` 文件置于 `~/.pandoc/templates/` 下 4 | + 在项目目录下运行 `sh pandoc/gen.sh` 5 | 6 | 注: 7 | 8 | + 字体选择放在了 `gen.sh` 中,请根据需要自行替换 9 | + 封面放在了 `algo.latex` 中,可自行修改 10 | + 需要安装 `pandoc`, `latex`, `python` 相关依赖 11 | 12 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## 关于模板库 2 | 3 | ![](https://github.com/F0RE1GNERS/template/workflows/build/badge.svg) 4 | 5 | [PDF 下载](https://F0RE1GNERS.github.io/template/template.pdf) 6 | 7 | 由 ECNU ~~现役~~ ~~半退役~~ 退役队伍 F0RE1GNERS 的队员维护,队伍 [wiki 链接](https://acm.ecnu.edu.cn/wiki/index.php?title=ECNU_Foreigners_(2018))([备用链接](https://eoj.i64d.com/wiki/index.php?title=ECNU_Foreigners_(2018)))。 8 | 9 | F0RE1GNERS 于 2019.04 的 43rd ICPC World Finals 后退役。[WF 的 TRD](https://F0RE1GNERS.github.io/template/WF-Team-Reference-Document.pdf) 仅供留念(内容略有删减)。 10 | 11 | F0RE1GNERS (IO%) 将会参加 2019 年的若干场区域赛,有幸与大家再度相会。 12 | 13 | ## 代码特性 14 | 15 | + 优先保证代码简洁和可读性,其次是常数 16 | + 数据结构使用指针(除了要卡空间的主席树) 17 | + 代码尽量用 `namespace` 封装 18 | + 轻度压行,不使用逗号压行 19 | + 使用 `template` 来复用代码 20 | + 代码符合 C++11 标准,且至少需要 C++11 21 | + 系统相关的部分只考虑 *unix 22 | 23 | 24 | ## 关于 PDF 25 | 26 | + 使用 pandoc 和 LaTeX 生成 27 | + 代码高亮使用 minted 28 | --------------------------------------------------------------------------------