广义串并联图学习笔记

广义串并联图定义为不包含同胚于 \(K_4\) 的子图的图。

平面图要求不包含同胚于 \(K_5\) 的子图,所以平面图不一定是广义串并联图。

换句话说,不存在四个点满足两两之间都存在边不相交的路径相连。


广义串并联图的性质是,我们可以通过 广义串并联图方法 将他缩成一个点。

具体来说,有三种不同的操作:

    1. 缩一度点,将度数为 1 的点连同边删去。
    1. 缩二度点,假设这个点出发的两条边分别到达 \(x,y\),我们删去这个点和原有的两条边,添加一条新边 \((x,y)\)
    1. 叠合重边,将两条重边整合成一条。

通过这三种方法交替操作,我们总能将任意一个广义串并联图缩成一个点。

在这个过程中,每一条边都代表了原图的一个连通子图。

通常的应用方法是在进行三种操作的过程中维护这条边对答案的贡献(方案数之类),最终缩成一个点的时候就可以直接计算答案。


例题:[SNOI2020] 生成树

给出一个广义串并联图,求生成树个数。

我们对于每一条边 \(e\) 维护一个 \(f_e,g_e\) 表示这条边的两端 连通/不连通 的方案数。

考虑进行三种操作的时候会如何变化:

    1. 缩一度点,这个时候两端必须连通,直接将答案乘上 \(f_e\)
    1. 缩二度点,假设删去这个点出发的两条边 \(x,y\) 加入一条新边 \(z\)

如果新边连通,要求原来的两条边均连通。如果新边不连通,原来的两条边恰好一条连通。

\[\begin{align*} f_{z}&=f_{x}f_{y} \\g_{z}&=f_{x}g_{y}+g_{x}f_{y} \end{align*} \]

    1. 叠合重边,将两条重边 \(x,y\) 整合成一条 \(z\)

如果新边连通,要求原来的两条中恰好一条连通。如果新边不连通,原来的两条边均不连通。

\[\begin{align*} f_{z}&=f_{x}g_{y}+g_{x}f_{y} \\g_{z}&=g_{x}g_{y} \end{align*} \]

通过这种操作,我们可以简单地维护答案。

代码
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>

const int N = 2e5 + 7, O = 998244353;
typedef long long i64;
#define rep(i,a,b) for(int i(a);i<=(b);++i)

namespace wyx {

int n, m;
std::map<int, int> g[N];
i64 c0[N], c1[N];
int deg[N];

inline void main() {
	std::cin >> n >> m;
	int new_m = 0;
	rep(i, 1, m) {
		int x, y; std::cin >> x >> y;
		auto& e = g[x][y];
		if(e) { ++c1[e]; } 
		else {
			++deg[x], ++deg[y];
			e = g[y][x] = ++new_m;
			c0[new_m] = c1[new_m] = 1;
		}
	}
	m = new_m;

	std::queue<int> q;
	rep(i, 1, n) { if(deg[i] <= 2) q.push(i); }	

	i64 ans = 1;
	while(!q.empty()) {
		int u = q.front(); q.pop();
		if(!deg[u]) continue;
		if(deg[u] == 1) {
			auto& [v, e] = *g[u].begin(); ans = ans * c1[e] %O;
			g[v].erase(u); if(--deg[v] <= 2) q.push(v);
		}
		if(deg[u] == 2) {
			auto& [x, ex] = *g[u].begin();
			auto& [y, ey] = *--g[u].end();
			g[x].erase(u), g[y].erase(u);
			std::tie(c0[ex], c1[ex]) = std::make_pair(
				(c0[ex] * c1[ey] + c1[ex] * c0[ey]) %O,
				c1[ex] * c1[ey] %O
			);
			auto& ez = g[x][y];
			if(ez) {
				std::tie(c0[ex], c1[ex]) = std::make_pair(
					c0[ex] * c0[ez] %O, 
					(c0[ex] * c1[ez] + c1[ex] * c0[ez]) %O 
				);
				if(--deg[x] <= 2) q.push(x);
				if(--deg[y] <= 2) q.push(y);
			}
			ez = g[y][x] = ex;	
		}
		deg[u] = 0;
	}

	std::cout << ans << "\n";
}

};

int main() {
	std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
	wyx::main();
}

例题 三染色

给出一个广义串并联图,要求对其进行三染色。

先模拟一遍广义串并联图方法,把操作顺序记下来之后倒序还原。


更加一般的方法

假如我们只有一个一般图,但是保证了 \(m-n \le k\)

那么我们可以同样地模拟广义串并联图方法,直到得到一个无法操作的子图。

由于每个点度数都 \(\ge 3\),所以 \(3n\ge 2m\)

又由于在操作过程中 \(m-n\) 不降,所以可以得到 \(n\le 2k, m\le 3k\)

\(k\) 较小的时候支持我们用暴力得到答案。

posted @ 2025-10-23 09:52  CuteNess  阅读(10)  评论(0)    收藏  举报