ARC140D 做题笔记
好题。(不过绝大部分题解全在瞎说)
看到
而最后每个连通块一定是一个基环树,那么统计连通块的数量就相当于统计基环树的数量。
既然有基环树,这种题绝对不能枚举然后求连通块数量,一定是枚举连通块求它在多少种情况中会出现。
首先先扫一遍,把确定的边用并查集建好,同时维护每个树的点数量,变数量,这样就能够方便判断它是不是一个基环树了。
从基环树森林上断掉任意条边,一定会形成若干颗森林和若干颗基环树,这点十分显然,对于断开后仍是基环树的情况,可以直接统计答案了(因为日后无论怎么连始终是一个连通块),此时记输入的
接着考虑树,树中一定是有一个点向外连的边还没有确定,如果将这个边连到输入时就有的基环树上,此时的答案我们已经在输入时统计完了,所以唯一剩下的情况就是树连到树上形成基环树。
此时仍然不好考虑,不妨枚举一些东西,枚举这个基环树是由
事实上,我们还要对于这
排列的数量为
因为
如果有
于是枚举完
此时发现枚举
但答案仅仅如此吗?并不是,因为还有其余的
至此,这道题做完了:
#include <bits/stdc++.h> #define int long long #define For(i, a, b) for (int i = (a); i <= (b); i ++) using namespace std; int n, ans, cnt, cnt_1; int a[2005], fac[2005], sum[2005]; int F[2005][2005]; const int mod = 998244353; bool f, vis[2005]; int fa[2005], sz[2005], edge[2005]; int q_pow (int x, int y) { if (y == 0) return 1; if (y & 1) return x * q_pow (x * x % mod, y >> 1) % mod; return q_pow (x * x % mod, y >> 1) % mod; } int find (int x) {return (fa[x] == x ? x : fa[x] = find (fa[x]) );} void merge (int x, int y) { int fx = find (x), fy = find (y); if (fx == fy) edge[fx] ++; else { fa[fy] = fx; sz[fx] += sz[fy]; edge[fx] += edge[fy] + 1; } } signed main () { fac[0] = 1; For (i, 1, 2000) { fac[i] = fac[i - 1] * i % mod; fa[i] = i; sz[i] = 1; } cin >> n; For (i, 1, n) { cin >> a[i]; if (a[i] == -1) { ++ cnt_1; continue; } else merge (i, a[i]); } For (i, 1, n) { if (fa[i] == i) { if (edge[i] == sz[i]) ans = (ans + q_pow (n, cnt_1) ) % mod; else sum[++ cnt] = sz[i]; } } F[0][0] = 1; For (i, 1, cnt) For (j, 0, i) { F[i][j] = F[i - 1][j]; if (j) F[i][j] = (F[i][j] + F[i - 1][j - 1] * sum[i]) % mod; } For (i, 1, cnt) { ans = (ans + F[cnt][i] * q_pow (n, cnt_1 - i) % mod * fac[i - 1]) % mod; } cout << ans; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异