题目大意:有\(n\)个储钱罐,每个储钱罐的钥匙都在另一个储钱罐中。求打开每一个储钱罐所需要砸开的最小数量。
\(Solution\):如果储钱罐\(x\)的钥匙在\(y\)中,那么就连边\(x\)->\(y\)。于是每个点的出度都是1。然后就变成了和NOIP 2015 Day1 T2几乎一样的题——Tarjan缩点或者DFS,统计强连通块数量即可。
——但是本题会卡内存,所以这么做会爆栈,MLE妥妥的。所以参考了网上的一种用并查集的“优越做法”。注意到每个点都在且仅在一个强连通分量(也就是若干个无交点的简单环),所以可以把它们直接当成连通分量做,并查集维护之即可。
// BZOJ 1529 #include#include using namespace std; #define N 1000005 int fa[N], n, y; int find(int x) { return fa[x]==x ? x : fa[x]=find(fa[x]); } #define read(x) scanf("%d", &x) #define rep(i,a,b) for (int i=a; i<=b; i++) int main() { read(n); int ans=n; rep(i,1,n) fa[i]=i; rep(x,1,n) { read(y); int fx=find(x), fy=find(y); if (fx!=fy) ans--; fa[fx]=fy; } printf("%d\n", ans); return 0; }