P4129 [SHOI2006]仙人掌
Orange_qwq · · 题解
前言
这里是题目。
主体代码没问题,高精度愣是搞了好久。。。
思路
这题我们首先要判断图是不是仙人掌,然后要求出把仙人掌进行删边之后得到的图仍然是连通图的方案数。
先看看这个方案数怎么求。
对于一个仙人掌图进行删边操作,显然我们只能删环上的边。对于每一个环,我们可以选择任意一条边删掉,也可以选择不删。设第
然后问题就变成了怎么求环和
我们可以用
那环的边数呢?我们构造出来生成树,如果有返租边,那么一定是先一直向下访问、构造,然后到了某个节点
剩下的问题是怎么判断图是不是仙人掌了。由图观察可知,如果 某个节点的子孙的返租边到了这个节点之上 与 这个节点发出的返租边 的和
您可能要问为啥不用 ,因为这个菜鸡认为
代码
注意最后的答案可能是一个很大很大的数。
我们要用高精度存答案。
可能要用压位高精,因为这个菜鸡 MLE 了好多发,最后把
void dfs(int x, int fa) {
++num;
int cnt = 0; // 求该节点所在环的个数
dfn[x] = low[x] = ++tot;
for (int i = he[x]; i; i = ne[i]) {
int y = e[i];
if (y == fa) continue;
if (!dfn[y]) { // 如果是树枝边
dep[y] = dep[x] + 1; // 计算深度
dfs(y, x);
low[x] = min(low[x], low[y]);// 求最大程度追溯的节点
if (low[y] < dfn[x]) ++cnt; // 有个儿子的返租边在 y 之上,注意这里是 < ,题目中说的是边
} else if (dfn[y] < dfn[x]) { // 是返租边
if (dep[x] - dep[y] > 1) ans = ans * (dep[x] - dep[y] + 2); // 计算答案
++cnt;
low[x] = min(low[x], dfn[y]);// 求最大程度追溯的节点
}
if (cnt == 2) ok = 0; // 不是仙人掌
}
}
int main() {
ans = 1;
n = read(), m = read();
for (int i = 1, t, lst; i <= m; ++i) {
t = read(), lst = read();
for (int j = 2, x; j <= t; ++j) {
x = read();
e[++tot] = x, ne[tot] = he[lst], he[lst] = tot;
e[++tot] = lst, ne[tot] = he[x], he[x] = tot;
lst = x;
}
}
tot = 0;
dfs(1, 1);
if (num != n) ok = 0; // 不连通
if (ok) ans.pr();
else puts("0");
return 0;
}
后记
建议先写主体部分再加高精板子,要不然很难知道是哪里出了问题。用 int 能有