NOIP 考前板子复习
点双
注意两个点,特判单点,是 son = 0 且 fa = 0,因为自环,还有弹栈弹到儿子节点处,因为点双不一定由割点弹出。
code
void dfs(int u, int la) {
int son = 0;
low[u] = dfn[u] = ++dfc;
sta[++top] = u;
for(int v : A[u]) {
if(!dfn[v]) {
++son;
dfs(v, u);
cmin(low[u], low[v]);
if(low[v] >= dfn[u]) {
++ scc;
while(true) {
int cur = sta[top];
dcc[scc].eb(cur);
--top;
if(cur == v) break;
// 为什么不能用 cur == u 的写法,因为当前点不一定是本割点的出点,8 型
}
dcc[scc].eb(u);
}
}
else if(v != la) cmin(low[u], dfn[v]);
}
if(la == 0 && son == 0) dcc[++scc].eb(u);
}
void tarjan() {
rep(i, 1, n) {
if(!dfn[i]) {
top = 0;
dfs(i, 0);
}
}
}
边双
可以用 pii 存边,注意是边不能走反向,即重边。
code
void dfs(int u, int la) {
stk[++ top] = u;
dfn[u] = low[u] = ++ dfc;
for(pii e : A[u]) {
if(e.fi == la) continue;
int v = e.se;
if(dfn[v]) cmin(low[u], dfn[v]);
else dfs(v, e.fi), cmin(low[u], low[v]);
}
if(dfn[u] != low[u]) return;
++ scc;
while(true) {
int cur = stk[top]; -- top;
S[scc].eb(cur);
if(cur == u) break;
}
}
void tarjan() {
rep(i, 1, n) {
if(!dfn[i]) {
dfs(i, -1);
}
}
}
割点
注意根节点的判断。
code
void dfs(int u, int la) {
int son = 0;
dfn[u] = low[u] = ++ dfc;
for(int v : A[u]) {
if(v == la) continue;
if(dfn[v]) cmin(low[u], dfn[v]);
else {
++ son;
dfs(v, u);
cmin(low[u], low[v]);
if(low[v] >= dfn[u]) {
cut[u] = 1;
}
}
}
if(son == 1 && la == 0) cut[u] = 0;
}
void tarjan() {
rep(i, 1, n) {
if(dfn[i]) continue;
dfs(i, 0);
}
}
割边
这个好像没有例题?
强连通分量
圆方树
看着 OI-Wiki 打了个板子。
code
void dfs(int u) {
low[u] = dfn[u] = ++ dfc;
stk[++ tp] = u;
for(int v : G[u]) {
if(!dfn[v]) {
dfs(v);
cmin(low[u], low[v]);
if(low[v] == dfn[u]) {
++ cnt;
for(int x = 0; x != v; -- tp) {
x = stk[tp];
T[cnt].eb(x);
T[x].eb(cnt);
}
T[cnt].eb(u);
T[u].eb(cnt);
}
}
else cmin(low[u], dfn[v]);
}
}
void solve() {
rd(n, m);
rep(i, 1, m) {
int u, v;
rd(u, v);
G[u].eb(v);
G[v].eb(u);
}
cnt = n;
rep(u, 1, n) if(!dfn[u]) dfs(u), -- tp;
rep(i, n + 1, cnt) {
printf("#id = %d, #size = %d, include:", i - n, (int) T[i].size());
for(int j : T[i]) {
printf("%d ", j);
}
puts("");
}
return;
}
铁人两项
还是照着 OI-Wiki 打了个板子。这个赋点权的方法很厉害啊。
code
void dfs(int u) {
low[u] = dfn[u] = ++ dfc;
stk[++ tp] = u;
++ num;
for(int v : G[u]) {
if(!dfn[v]) {
dfs(v);
cmin(low[u], low[v]);
if(low[v] == dfn[u]) {
++ cnt;
for(int x = 0; x != v; -- tp) {
x = stk[tp];
T[cnt].eb(x);
T[x].eb(cnt);
}
T[cnt].eb(u);
T[u].eb(cnt);
val[cnt] = T[cnt].size();
}
}
else cmin(low[u], dfn[v]);
}
}
void dfs1(int u, int la) {
vis[u] = 1;
sz[u] = (u <= n);
for(int v : T[u]) {
if(v == la) continue;
dfs1(v, u);
ans += 2LL * val[u] * sz[u] * sz[v];
sz[u] += sz[v];
}
ans += 2LL * val[u] * sz[u] * (num - sz[u]);
}
void solve() {
rd(n, m);
rep(i, 1, n) {val[i] = -1;}
rep(i, 1, m) {
int u, v;
rd(u, v);
G[u].eb(v);
G[v].eb(u);
}
cnt = n;
rep(u, 1, n) {
if(!dfn[u]) {
num = 0;
dfs(u);
-- tp;
dfs1(u, 0);
}
}
printf("%lld\n", ans);
return;
}
网络流
树链剖分
LCT
主席树
分块
莫队
-
带修莫队
-
回滚莫队/不删除莫队
-
树上莫队
平衡树
-
FHQ Treap
-
Splay
旋转卡壳
打的也不是板子,是 这题
code
ll dist(int i1, int i2) {
return 1LL * (p[i1].x - p[i2].x) * (p[i1].x - p[i2].x) + 1LL * (p[i1].y - p[i2].y) * (p[i1].y - p[i2].y);
}
ll cross(int p0, int p1, int p2) {
return 1LL * (p[p1].x - p[p0].x) * (p[p2].y - p[p0].y) - 1LL * (p[p2].x - p[p0].x) * (p[p1].y - p[p0].y);
}
void In(int a, int b) {
pq.push(dist(a, b));
if((int) pq.size() > k) pq.pop();
}
bool cmp(int a, int b) {
return p[a].x == p[b].x ? p[a].y < p[b].y : p[a].x < p[b].x;
}
int nxt(int x) {return x % top + 1;}
pii calc() {
o_n = 0;
rep(i, 1, n) {
if(!vis[i]) {
o[++ o_n] = i;
}
}
sort(o + 1, o + o_n + 1, cmp);
sta[top = 1] = o[1];
rep(i, 2, o_n) {
while(top > 1 && cross(sta[top - 1], sta[top], o[i]) <= 0) {
-- top;
}
sta[++ top] = o[i];
}
int pre = top;
per(i, o_n - 1, 1) {
while(top > pre && cross(sta[top - 1], sta[top], o[i]) <= 0) {
-- top;
}
sta[++ top] = o[i];
}
-- top;
if(top == 2) return mp(sta[1], sta[2]);
// 前面都是求凸包模板
// 接下来是找直径模板
int cur = 1;
while(cross(sta[top], sta[1], sta[nxt(cur)]) > cross(sta[top], sta[1], sta[cur])) {
cur = nxt(cur);
}
int a = sta[cur], b;
if(dist(sta[cur], sta[1]) > dist(sta[cur], sta[top])) b = sta[1];
else b = sta[top];
// 找以 sta[1], sta[top] 这条边为对边的对踵点
// 一个个找,比较
rep(i, 1, top - 1) {
while(cross(sta[i], sta[i + 1], sta[nxt(cur)]) > cross(sta[i], sta[i + 1], sta[cur])) {
cur = nxt(cur);
}
// 两个都要比较一下
if(dist(sta[cur], sta[i]) > dist(a, b)) {
a = sta[cur];
b = sta[i];
}
if(dist(sta[cur], sta[i + 1]) > dist(a, b)) {
a = sta[cur];
b = sta[i + 1];
}
}
return mp(a, b);
}
void solve() {
rd(n, k);
// printf("%d %d\n", n, k);
rep(i, 1, n) {
rd(p[i].x, p[i].y);
}
rep(i, 1, min(k, n / 2)) {
pii res = calc();
// 处理的是凸包
// 将凸包上所有距离较远的加入
int a = res.fi;
int b = res.se;
In(a, b);
vis[a] = vis[b] = 1;
rep(j, 1, n) {
if(!vis[j]) {
In(a, j);
In(b, j);
}
}
}
printf("%lld", pq.top());
}
点分治
李超线段树
code

浙公网安备 33010602011771号