NOIP 考前板子复习

点双

注意两个点,特判单点,是 son = 0fa = 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;
}

铁人两项

LUOGU
LOJ

还是照着 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
posted @ 2023-11-17 06:37  SkyMaths  阅读(17)  评论(0)    收藏  举报