BZOJ 1529 - 并查集

题目大意:有\(n\)个储钱罐,每个储钱罐的钥匙都在另一个储钱罐中。求打开每一个储钱罐所需要砸开的最小数量。

\(Solution\):如果储钱罐\(x\)的钥匙在\(y\)中,那么就连边\(x\)->\(y\)。于是每个点的出度都是1。然后就变成了和NOIP 2015 Day1 T2几乎一样的题——Tarjan缩点或者DFS,统计强连通块数量即可。

——但是本题会卡内存,所以这么做会爆栈,MLE妥妥的。所以参考了网上的一种用并查集的“优越做法”。注意到每个点都在且仅在一个强连通分量(也就是若干个无交点的简单环),所以可以把它们直接当成连通分量做,并查集维护之即可。

    // BZOJ 1529
    
    #include <cstdio>
    #include <cstring>
    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;
    }
posted @ 2016-01-14 21:49  Armeria  阅读(245)  评论(0编辑  收藏  举报