定义

  • semi[x] 半必经点: $x$ 的祖先 $z$ 中,能不经过 $z$ 和 $x$ 之间的树上的点而到达 $x$ 的点中深度最小的。
  • idom[x] 最近必经点:就是深度最大的根到 $x$ 的必经点。

其它的证明看不懂 T^T。

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
vector<int> G[maxn], rG[maxn];
vector<int> dt[maxn];
// 原图,反图,支配树

namespace DomTree {
int fa[maxn], idx[maxn], tot, ridx[maxn];
int c[maxn], best[maxn], semi[maxn], idom[maxn];
void clear(int n) {
tot = 0;
fill(c, c + n + 1, -1);
fill(idx, idx + n + 1, 0);
for (int i = 0; i <= n; i++) {
dt[i].clear();
semi[i] = best[i] = i;
}
}
void dfs(int u) {
idx[u] = ++tot; ridx[tot] = u;
for (int& v: G[u]) {
if (idx[v]) continue;
fa[v] = u; dfs(v);
}
}
int fix(int x) {
if (c[x] == -1) return x;
int &f = c[x], rt = fix(f);
if (idx[semi[best[x]]] > idx[semi[best[f]]]) best[x] = best[f];
return f = rt;
}
void build(int rt) {
dfs(rt);
for (int i = tot; i >= 1; i--) {
int x = ridx[i], mn = tot + 1;
for (int& u: rG[x]) {
if (!idx[u]) continue;
fix(u); mn = min(mn, idx[semi[best[u]]]);
}
c[x] = fa[x];
dt[semi[x] = ridx[mn]].push_back(x);
x = ridx[i - 1];
for (int& u: dt[x]) {
fix(u);
if (semi[best[u]] != x) idom[u] = best[u];
else idom[u] = x;
}
dt[x].clear();
}
for (int i = 2; i <= tot; i++) {
int u = ridx[i];
if (idom[u] != semi[u]) idom[u] = idom[idom[u]];
dt[idom[u]].push_back(u);
}
}
}
阅读全文 »

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
namespace pam {
int sz, tot, last;
int ch[maxn][26], len[maxn], fail[maxn];
int cnt[maxn], dep[maxn];
char s[maxn];
int node(int l) {
sz++; ms(ch[sz], 0);
len[sz] = l; fail[sz] = cnt[sz] = dep[sz] = 0;
return sz;
}
void clear() {
sz = -1; last = 0;
s[tot = 0] = '$';
node(0); node(-1);
fail[0] = 1;
}
int getfail(int x) {
while (s[tot - len[x] - 1] != s[tot]) x = fail[x];
return x;
}
void insert(char c) {
s[++tot] = c;
int now = getfail(last);
if (!ch[now][c - 'a']) {
int x = node(len[now] + 2);
fail[x] = ch[getfail(fail[now])][c - 'a'];
dep[x] = dep[fail[x]] + 1;
ch[now][c - 'a'] = x;
}
last = ch[now][c - 'a'];
cnt[last]++;
}
void count() {
for (int i = sz; i >= 0; i--) {
cnt[fail[i]] += cnt[i];
}
}
}
阅读全文 »

A Remove a Progression

B Yet Another Crosses Problem

C From S To T

D 1-2-K Game

两个人游戏,从 $n$ 数到 $0$,每次选择 $-1,-2,-k$ 三种中一个,无法操作者为负,判断是否先手必胜。

必胜态 / 必败态打表观察。

E Count The Rectangles

给了一堆水平线和竖直线,求能够拼成多少个矩形。

枚举右边界,扣出与右边界相交的横线,枚举左边界,从左到右扫描线,树状数组维护横线的纵坐标出现位置。

时间复杂度 $\mathcal{O}(n^2 \log n)$。

F Crossword Expert

总时间为 $T$,依次发生 $n$ 个事件,每个事件时长为 $t_i$,每个事件的时长各有一半的概率不变和变成 $t_i+1$,求发生事件次数的期望。

设发生事件 $i$ 至少需要时间 $pre$,即前缀和,最多花费时间 $pre+i$,那么事件 $i$ 发生的概率为 $\sum_{j=0}^{T-pre}{ i \choose j } / 2^i$。

因此题意变成求一堆这样的组合数,分治 NTT 莫队即可。

阅读全文 »