# Trick

## 基础

### IO

• 模板
namespace io {
const int SIZE = (1 << 21) + 1;
char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55];
int f, qr;
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
inline void flush() {
fwrite(obuf, 1, oS - obuf, stdout);
oS = obuf;
}
inline void putc(char x) {
*oS++ = x;
if (oS == oT) flush();
}
template <class I>
inline void rd(I &x) {
for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15);
x *= f;
}
template <class I>
inline void print(I x) {
if (!x) putc('0');
if (x < 0) putc('-'), x = -x;
while (x) qu[++qr] = x % 10 + '0', x /= 10;
while (qr) putc(qu[qr--]);
putc('\n');
}
struct Flusher_ {
~Flusher_() {
flush();
}
} io_flusher_;
}
using io::rd;
using io::print;

### 二分

• 求单调序列中数 $x$ 的个数
upper_bound(a + 1, a + n + 1, x) - lower_bound(a + 1, a + n + 1, x);

• 单个元素映射多维随机权值

• set实现Hash表

## 字符串

### SA

• 模板
namespace SA {
const int N = ...;
int n, m = ..., sa[N], rk[N], tp[N], tx[N], he[N], st[N][...];
char s[N];

inline void tsort() {
for (int i = 1; i <= m; i++) tx[i] = 0;
for (int i = 1; i <= n; i++) ++tx[rk[i]];
for (int i = 1; i <= m; i++) tx[i] += tx[i-1];
for (int i = n; i; i--) sa[tx[rk[tp[i]]]--] = tp[i];
}

inline bool pd(int i, int w) {
return tp[sa[i-1]] == tp[sa[i]] && tp[sa[i-1]+w] == tp[sa[i]+w];
}

inline void main() {
for (int i = 1; i <= n; i++) rk[i] = s[i] - 'a' + 1, tp[i] = i;
tsort();
for (int w = 1, p = 0; p < n; w <<= 1, m = p) {
p = 0;
for (int i = 1; i <= w; i++) tp[++p] = n - w + i;
for (int i = 1; i <= n; i++) if (sa[i] > w) tp[++p] = sa[i] - w;
tsort(), swap(rk, tp), rk[sa[1]] = p = 1;
for (int i = 2; i <= n; i++) rk[sa[i]] = pd(i, w) ? p : ++p;
}
int p = 0;
for (int i = 1; i <= n; i++) {
if (p) --p;
int j = sa[rk[i]-1];
while (s[i+p] == s[j+p]) ++p;
he[rk[i]] = p;
}
for (int i = 1; i <= n; i++) st[i][0] = he[i];
int w = log(n) / log(2);
for (int k = 1; k <= w; k++)
for (int i = 1; i + (1 << k) - 1 <= n; i++)
st[i][k] = min(st[i][k-1], st[i+(1<<(k-1))][k-1]);
}

inline int get(int l, int r) {
int k = log(r - l + 1) / log(2);
return min(st[l][k], st[r-(1<<k)+1][k]);
}
}

## 数据结构

### Treap

• 模板
namespace Treap {
const int N = ..., inf = 1e9;
int t, rt;
struct T {
int l, r, x, k, c, s;
} a[N];

a[++t].x = x;
a[t].k = rand();
a[t].c = a[t].s = 1;
return t;
}

inline void upd(int p) {
a[p].s = a[a[p].l].s + a[a[p].r].s + a[p].c;
}

inline void zig(int &p) {
int q = a[p].l;
a[p].l = a[q].r;
a[q].r = p;
p = q;
upd(a[p].r);
upd(p);
}

inline void zag(int &p) {
int q = a[p].r;
a[p].r = a[q].l;
a[q].l = p;
p = q;
upd(a[p].l);
upd(p);
}

void ins(int &p, int x) {
if (!p) {
return;
}
if (x == a[p].x) {
a[p].c++;
upd(p);
return;
}
if (x < a[p].x) {
ins(a[p].l, x);
if (a[p].k < a[a[p].l].k) zig(p);
} else {
ins(a[p].r, x);
if (a[p].k < a[a[p].r].k) zag(p);
}
upd(p);
}

void del(int &p, int x) {
if (!p) return;
if (x == a[p].x) {
if (a[p].c > 1) {
--a[p].c;
upd(p);
return;
}
if (a[p].l || a[p].r) {
if (!a[p].r || a[a[p].l].k > a[a[p].r].k) {
zig(p);
del(a[p].r, x);
} else {
zag(p);
del(a[p].l, x);
}
upd(p);
} else p = 0;
return;
}
x < a[p].x ? del(a[p].l, x) : del(a[p].r, x);
upd(p);
}

int rnk(int p, int x) {
if (!p) return 0;
if (x == a[p].x) return a[a[p].l].s + 1;
if (x < a[p].x) return rnk(a[p].l, x);
return rnk(a[p].r, x) + a[a[p].l].s + a[p].c;
}

inline int askrnk(int p, int x) {
return rnk(p, x) - 1;
}

int kth(int p, int k) {
if (!p) return inf;
if (a[a[p].l].s >= k) return kth(a[p].l, k);
if (a[a[p].l].s + a[p].c >= k) return a[p].x;
return kth(a[p].r, k - a[a[p].l].s - a[p].c);
}

inline int askkth(int p, int k) {
return kth(p, k + 1);
}

inline int pre(int x) {
int ans = 1, p = rt;
while (p) {
if (x == a[p].x) {
if (a[p].l > 0) {
p = a[p].l;
while (a[p].r > 0) p = a[p].r;
ans = p;
}
break;
}
if (a[p].x < x && a[p].x > a[ans].x) ans = p;
p = x < a[p].x ? a[p].l : a[p].r;
}
return a[ans].x;
}

inline int nxt(int x) {
int ans = 2, p = rt;
while (p) {
if (x == a[p].x) {
if (a[p].r > 0) {
p = a[p].r;
while (a[p].l > 0) p = a[p].l;
ans = p;
}
break;
}
if (a[p].x > x && a[p].x < a[ans].x) ans = p;
p = x < a[p].x ? a[p].l : a[p].r;
}
return a[ans].x;
}

inline int build() {
srand(time(0));
upd(rt);
return rt;
}
}

### LCT

• 模板
namespace LCT {
#define nort(x) (t[t[x].f].ch[0] == x || t[t[x].f].ch[1] == x)
#define get(x) (t[t[x].f].ch[1] == x)
#define rev(x) swap(t[x].ch[0], t[x].ch[1]), t[x].r ^= 1
const int N = ...;
struct T {
int f, ch[2], r, s, ...;
} t[N];

inline void upd(int x) {
t[x].s = t[t[x].ch[0]].s + t[t[x].ch[1]].s + 1;
...
}

if (t[x].r) {
if (t[x].ch[0]) rev(t[x].ch[0]);
if (t[x].ch[1]) rev(t[x].ch[1]);
t[x].r = 0;
}
...
}

inline void rot(int x) {
int y = t[x].f, z = t[y].f, k = get(x), w = get(y);
if (t[x].ch[k^1]) t[t[x].ch[k^1]].f = y;
t[y].ch[k] = t[x].ch[k^1];
t[x].f = z;
if (nort(y)) t[z].ch[w] = x;
t[y].f = x;
t[x].ch[k^1] = y;
upd(y);
}

inline void splay(int x) {
int y = x;
stack<int> st;
st.push(y);
while (nort(y)) {
y = t[y].f;
st.push(y);
}
while (st.size()) {
st.pop();
}
while (nort(x)) {
y = t[x].f;
if (nort(y)) rot(get(x) == get(y) ? y : x);
rot(x);
}
upd(x);
}

inline void access(int x) {
int y = 0;
while (x) {
splay(x);
t[x].ch[1] = y;
upd(x);
y = x;
x = t[y].f;
}
}

inline void mkrt(int x) {
access(x);
splay(x);
rev(x);
}

inline int fdrt(int x) {
access(x);
splay(x);
while (t[x].ch[0]) {
x = t[x].ch[0];
}
splay(x);
return x;
}

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

inline void link(int x, int y) {
mkrt(x);
if (fdrt(y) != x) t[x].f = y;
}

inline void cut(int x, int y) {
mkrt(x);
if (fdrt(y) == x && t[y].f == x && !t[y].ch[0]) {
t[y].f = t[x].ch[1] = 0;
upd(x);
}
}
}

### 去log

• 可持久化

• 离线（CDQ分治/整体二分）

### 偏序转化

• 记录前驱（上一次出现位置）

• 维护值域

• 树上DFS序

### 点分治

• 注意递归时保存树的重心（递归时树的重心会改变）

## 图论

• 点边转化

• 连边模板

const int N = ..., M = ...;
int Head[N], Edge[M], Leng[M], Next[M], tot = 1;

inline void add(int x, int y, int z) {
Edge[++tot] = y;
Leng[tot] = z;
}

### 树

• 无根树不要设 $1$ 为根

• 多个点LCA的最深点是dfs序相邻两点的LCA，最浅点是dfs序最远两点的LCA

### 网络流

• Dinic模板
namespace Dinic {
int S = N - 1, T = N - 2, d[N];

inline bool bfs() {
memset(d, 0, sizeof(d));
queue<int> q;
q.push(S);
d[S] = 1;
while (q.size()) {
int x = q.front();
q.pop();
for (int i = Head[x]; i; i = Next[i]) {
int y = Edge[i], z = Leng[i];
if (d[y] || !z) continue;
q.push(y);
d[y] = d[x] + 1;
if (y == T) return 1;
}
}
return 0;
}

int dinic(int x, int flow) {
if (x == T) return flow;
int rest = flow;
for (int i = Head[x]; i && rest; i = Next[i]) {
int y = Edge[i], z = Leng[i];
if (d[y] != d[x] + 1 || !z) continue;
int k = dinic(y, min(rest, z));
if (!k) d[y] = 0;
else {
Leng[i] -= k;
Leng[i^1] += k;
rest -= k;
}
}
return flow - rest;
}

inline int work() {
int now = 0, maxflow = 0;
while (bfs())
while ((now = dinic(S, inf)))
maxflow += now;
return maxflow;
}
}
• 权值有正有负，重复选只算一次，选了某些东西就得选其他东西——最大权闭合子图

## 动态规划

### 期望DP

• 逆序倒推

• 分离随机变量（利用期望的线性性）

### 状压DP

• 枚举子集
for (int i = s; i; i = (i - 1) & s)

## 数学

### 数论&组合

• 快速幂
#define ll long long

inline int ksm(int a, ll b, int p) {
int c = 1 % p;
while (b) {
if (b & 1) c = (ll)c * a % p;
a = (ll)a * a % p, b >>= 1;
}
return c;
}
• 欧几里得算法
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
• 扩展欧几里得算法
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, x, y), z = x;
x = y, y = z - y * (a / b);
return d;
}
• 欧拉函数&莫比乌斯函数线性筛
int miu[N], phi[N], v[N], p[N], c;

inline void prework(int n) {
miu[1] = phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!v[i]) {
v[i] = p[++c] = i;
miu[i] = -1;
phi[i] = i - 1;
}
for (int j = 1; j <= c && p[j] * i <= n && p[j] <= v[i]; j++) {
v[p[j]*i] = p[j];
miu[p[j]*i] = p[j] == v[i] ? 0 : -miu[i];
phi[p[j]*i] = phi[i] * (p[j] == v[i] ? p[j] : p[j] - 1);
}
}
}
• $\sum_{d=1}^{n}\ \lfloor \frac{n}{d} \rfloor = O(n\ log\ n)$

• $\varphi *1 = id$

• $\mu * 1 = \epsilon$

• $d(ij) = \sum_{x|i}\ \sum_{y|j}\ [gcd(x,y)==1]$

• 杜教筛：递归的 $x$ 如果不超过 $n ^ \frac{2}{3}$ 就查表，否则存到 $ff[n/x]$ 的位置

• 错排公式： $f_1 = 0, f_2 = 1, f_i = (i - 1)(f_{i-1} + f_{i-2})$

### 矩阵

• 模板
#define ll long long

namespace matrix {
const int N = ..., P = ...;
struct mat {
int n, m, a[N][N];
inline mat() {
memset(a, 0, sizeof(a));
}

inline void init() {
for (int i = 1; i <= n; i++) a[i][i] = 1;
}
};

inline mat operator * (const mat x, const mat y) {
mat z;
z.n = x.n, z.m = y.m;
int p = x.m;
for (int i = 1; i <= z.n; i++)
for (int k = 1; k <= p; k++)
for (int j = 1; j <= z.m; j++)
z.a[i][j] = (ll)(z.a[i][j] + (ll)x.a[i][k] * y.a[k][j] % P) % P;
return z;
}

inline mat ksm(mat x, ll y) {
mat z;
z.n = z.m = x.n;
z.init();
while (y) {
if (y & 1) z = z * x;
x = x * x;
y >>= 1;
}
return z;
}
}

### 高斯消元

• 模板
const int N = ...;
const double eps = 1e-10;
double a[N][N], b[N];

inline void Gauss(int n) {
for (int i = 1; i <= n; i++) {
int now = i;
for (int j = i + 1; j <= n; j++)
if (fabs(a[j][i]) > eps) now = j;
if (now != i) {
for (int j = 1; j <= n; j++)
swap(a[i][j], a[now][j]);
swap(b[i], b[now]);
}
for (int j = 1; j <= n; j++) {
if (i == j) continue;
double rate = a[j][i] / a[i][i];
for (int k = i; k <= n; k++)
a[j][k] -= a[i][k] * rate;
b[j] -= b[i] * rate;
}
}
}

### 多项式

• FFT模板
namespace FFT {
const int N = ...;
const double pi = acos(-1.0);
struct I {
double x, y;
inline I(double xx = 0, double yy = 0) {
x = xx, y = yy;
}
inline I operator + (const I a) const {
return I(x + a.x, y + a.y);
}
inline I operator - (const I a) const {
return I(x - a.x, y - a.y);
}
inline I operator * (const I a) const {
return I(x * a.x - y * a.y, x * a.y + y * a.x);
}
} a[N], b[N];
int n, m, k = 1, l, r[N];

inline void fft(I *z, int o) {
for (int i = 0; i < k; i++)
if (i < r[i]) swap(z[i], z[r[i]]);
for (int mid = 1; mid < k; mid <<= 1) {
I W = I(cos(pi / mid), o * sin(pi / mid));
for (int i = 0; i < k; i += mid << 1) {
I w = I(1, 0);
for (int j = 0; j < mid; j++, w = w * W) {
I x = z[i+j], y = w * z[i+j+mid];
z[i+j] = x + y;
z[i+j+mid] = x - y;
}
}
}
}

inline void work() {
while (k <= n + m) k <<= 1, ++l;
for (int i = 0; i < k; i++)
r[i] = (r[i>>1] >> 1) | ((i & 1) << (l - 1));
fft(a, 1);
fft(b, 1);
for (int i = 0; i < k; i++) a[i] = a[i] * b[i];
fft(a, -1);
for (int i = 0; i <= n + m; i++) a[i].x = abs(a[i].x) / k;
}
}
• NTT模板
namespace NTT {
const int N = ..., P = ..., G = ...;
int n, m, a[N], b[N], k = 1, l, r[N], invk, invG;

inline int ksm(int a, int b) {
int c = 1;
while (b) {
if (b & 1) c = (ll)c * a % P;
a = (ll)a * a % P, b >>= 1;
}
return c;
}

inline void ntt(int *z, int o) {
for (int i = 0; i < k; i++)
if (i < r[i]) swap(z[i], z[r[i]]);
for (int mid = 1; mid < k; mid <<= 1) {
int W = ksm(o == 1 ? G : invG, (P - 1) / (mid << 1));
for (int i = 0; i < k; i += mid << 1)
for (int j = 0, w = 1; j < mid; j++, w = (ll)w * W % P) {
int x = z[i+j], y = (ll)w * z[i+j+mid] % P;
z[i+j] = (x + y) % P;
z[i+j+mid] = (x - y + P) % P;
}
}
}

inline void work() {
while (k <= n + m) k <<= 1, ++l;
invk = ksm(k, P - 2), invG = ksm(G, P - 2);
for (int i = 0; i < k; i++)
r[i] = (r[i>>1] >> 1) | ((i & 1) << (l - 1));
ntt(a, 1);
ntt(b, 1);
for (int i = 0; i < k; i++) a[i] = (ll)a[i] * b[i] % P;
ntt(a, -1);
for (int i = 0; i <= n + m; i++) a[i] = (ll)a[i] * invk % P;
}
}
• 翻转卷积

posted @ 2019-03-19 16:09 xht37 阅读(...) 评论(...) 编辑 收藏