1 条题解

  • 0
    @ 2025-8-24 21:56:47

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Orange_qwq
    废物

    搬运于2025-08-24 21:56:47,当前版本为作者最后更新于2023-01-17 23:52:48,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    前言

    这里是题目。

    主体代码没问题,高精度愣是搞了好久。。。

    思路

    这题我们首先要判断图是不是仙人掌,然后要求出把仙人掌进行删边之后得到的图仍然是连通图的方案数。

    先看看这个方案数怎么求。

    对于一个仙人掌图进行删边操作,显然我们只能删环上的边。对于每一个环,我们可以选择任意一条边删掉,也可以选择不删。设第 ii 个环的边数为 numinum_i,我们可以这样计算得到答案 ansans

    ans=i=1环的总数numi+1ans = \prod\limits_{i=1}^{\texttt{环的总数}}num_i + 1

    然后问题就变成了怎么求环和 numinum_i

    我们可以用 dfs\text{dfs},构造出图的生成树。在 dfs\text{dfs} 的过程中,我们可以记录下每个节点 xx 的初始访问时间 dfnxdfn_x 和能够往上最大程度追溯到的节点 lowxlow_x。如果走了返租边(访问到了访问过的节点),那么说明出现了环。

    那环的边数呢?我们构造出来生成树,如果有返租边,那么一定是先一直向下访问、构造,然后到了某个节点 aa 就突然往上到了另一个节点 bb。由于我们一直向下构造,再加上一条边回到 bb,出现了一个环,所以这个环的边数就是我们从 bb 一直向下走到 aa 走的边数加上这条返租边。这个边数我们可以用深度的差得到,设第 ii 个节点的深度为 depidep_i,则环的边数为 depbdepa+1dep_b - dep_a + 1

    剩下的问题是怎么判断图是不是仙人掌了。由图观察可知,如果 某个节点的子孙的返租边到了这个节点之上 与 这个节点发出的返租边 的和 2\ge 2,那么就说明这个节点在两个环里了,该图不是仙人掌。

    您可能要问为啥不用 bfs\text{bfs},因为这个菜鸡认为 bfs\text{bfs} 有点难搞,并且比较擅长 dfs\text{dfs} dfs\text{dfs} 可以构造出生成树,比较好处理点或边出现的顺序关系(例如可以处理深度,利用 dfndfnlowlow 处理环),所以选择用 dfs\text{dfs}

    代码

    注意最后的答案可能是一个很大很大的数。

    我们要用高精度存答案。

    可能要用压位高精,因为这个菜鸡 MLE 了好多发,最后把 basebase 改成 1e141e14 才过的。

    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 能有 7070 分。

    • 1

    信息

    ID
    3082
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者