Bzoj1529/POI2005 ska Piggy banks

题目:ska Piggy banks

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1529

题目描述:Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,问最少要打破多少个存钱罐.

分析:

  (1)算法一:如果存钱罐i有存钱罐j的钥匙,就建一条(i->j)的有向边。然后就是缩点重构图后统计入度为0的连通块个数。最后就是华丽丽的Memory_Limit_Exceed了。

  (2)算法二:如果存钱罐i的钥匙在存钱罐j里,就建一条(i->j)的有向边。然后就是缩点重构图后统计出度为0的连通块个数。由于所有点的出度均为1(即只有一条出边),可以用一个数组来保存每个点的出边,这样可以比建邻接表保存图节省空间。结果Accept。

  (3)算法三:观察算法二建的图,分析该图的每一个子图,发现每一个子图有且只有一个环(圈),缩点后,这个环(圈)出度为0。问题转化为求一张图有几个连通块。考虑并查集。结果Accept。

代码:

这题IO量比较大,可以考虑读入优化,但我比较懒,不想写。

算法一:不想码。

算法二:

 1 #include <cstdio>
 2 #include <algorithm>
 3 int To[1000005];
 4 int TarjanTime,Dfn[1000005],Low[1000005],Stack[1000005];
 5 int Col[1000005],Out[1000005];
 6 void Tarjan(int v){
 7     Dfn[v]=Low[v]=++TarjanTime;
 8     Stack[++Stack[0]]=v;
 9     int u=To[v];
10     if(!Dfn[u]){
11         Tarjan(u);Low[v]=std::min(Low[v],Low[u]);
12     }else if(!Col[u])Low[v]=std::min(Low[v],Dfn[u]);
13     if(Dfn[v]==Low[v]){
14         ++Col[0];
15         for(;;){
16             u=Stack[Stack[0]--];
17             Col[u]=Col[0];
18             if(u==v)break;
19         }
20     }
21 }
22 int main(){
23     //freopen("in.txt","r",stdin);
24     //freopen("out.txt","w",stdout);
25     int n,ans;
26     scanf("%d",&n);
27     for(int i=1;i<=n;++i)scanf("%d",&To[i]);
28     for(int i=1;i<=n;++i)
29         if(!Dfn[i])Tarjan(i);
30     for(int i=1;i<=n;++i)
31         if(Col[i]!=Col[To[i]])
32             ++Out[Col[i]];
33     for(int i=1;i<=Col[0];++i)
34         if(!Out[i])++ans;
35     printf("%d",ans);
36     //fclose(stdin);fclose(stdout);
37     return 0;
38 }
39 

算法三:

 1 #include <cstdio>
 2 int Set[1000005];
 3 int GetSet(int x){return Set[x]==x?x:Set[x]=GetSet(Set[x]);}
 4 int main(){
 5     //freopen("in.txt","r",stdin);
 6     //freopen("out.txt","w",stdout);
 7     int n,ans=0;
 8     scanf("%d",&n);
 9     for(int i=1;i<=n;++i)Set[i]=i;
10     for(int i=1,to;i<=n;++i){
11         scanf("%d",&to);
12         Set[GetSet(i)]=GetSet(to);
13     }
14     for(int i=1;i<=n;++i)
15         ans+=(Set[i]==i);
16     printf("%d",ans);
17     //fclose(stdin);fclose(stdout);
18     return 0;
19 }
时间: 2024-10-08 18:24:03

Bzoj1529/POI2005 ska Piggy banks的相关文章

[POI2005]ska Piggy banks|并查集|tarjan

1529: [POI2005]ska Piggy banks Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 973  Solved: 445[Submit][Status][Discuss] Description Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,

【BZOJ】1529 [POI2005]ska Piggy banks

[算法](强连通分量)并查集 [题解] 1.用tarjan计算强连通分量并缩点,在新图中找入度为0的点的个数就是答案. 但是,会爆内存(题目内存限制64MB). 2.用并查集,最后从1到n统计fa[i]==i的数量即是答案. (tarjan) #include<cstdio> #include<algorithm> using namespace std; const int maxn=1000010; struct edge{int u,v,from;}e[maxn]; int

BZOJ 1529: [POI2005]ska Piggy banks [并查集]

题意: Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,问最少要打破多少个存钱罐. 每个存钱罐只有一把钥匙... 和那道洛谷3月月赛R1T3长的一样的有向连通环套树... 任意连通分量环上一个点都可以到达全部点 #include <iostream> #include <cstdio> #inc

BZOJ 1529 [POI2005]ska Piggy banks(并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1529 [题目大意] 给出一张n个点n条边的有向图,问选取几个点为起点可以遍历全图 [题解] 由于是n条边,因此图为基环森林,选取环上点一定可以到达连通块内所有点, 因此只要统计连通块个数即可. [代码] #include <cstdio> #include <algorithm> using namespace std; const int N=1000100; int

【BZOJ】【1529】 【POI2005】ska Piggy banks

本来以为是tarjan缩点……但是64MB的空间根本不足以存下原图和缩点后的新图.所以呢……并查集= = orz hzwer MLE的tarjan: 1 /************************************************************** 2 Problem: 1529 3 User: Tunix 4 Language: C++ 5 Result: Memory_Limit_Exceed 6 *******************************

BZOJ 1529 ska piggy banks

1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4   5 #define up(a,b,c) for(register int c=a;c<=b;c++) 6   7 int n,fa[1000005],x,tot; 8   9 inline int Read(){ 10     int x = 0,f = 1; 11     char ch = getchar(); 12     

Sicily 1350. Piggy banks 解题报告

题目:1350. Piggy banks 思路: 首先把每个钥匙的位置存进key_positions[]中,然后从第一个bank开始,用不同的color给它们分组.比如第一个bank的钥匙在第二个bank中,那么可以直接先开第二个,第二个钥匙在第四个bank中,同样可以先开第四个,以此类推,直到某个钥匙出现在前面的同一组的某个bank中则需要打破一个.visited数组初始化为0,然后访问过后标记为颜色的编号.第21行visited[cur] == color只有找到颜色相同即在同一组的才需要打

[Poi2005]Piggy Banks小猪存钱罐

题目描述 Byteazar有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,问最少要打破多少个存钱罐. 输入格式 第一行一个整数 N (1 <= N <= 1.000.000)表示存钱罐的总数. 接下来每行一个整数,第 i+1行的整数代表第i个存钱罐的钥匙放置的存钱罐编号. 输出格式 一个整数表示最少打破多少个存钱罐. 分析题目

【并查集&amp;&amp;带权并查集】BZOJ3296&amp;&amp;POJ1182

bzoj1529[POI2005]ska Piggy banks [题目大意] n头奶牛m种语言,每种奶牛分别掌握一些语言.问至少再让奶牛多学多少种语言,才能使得它们能够直接或间接交流? [思路] (n+m)个点,奶牛学会某种语言就合并它和语言的节点.并查集维护联通块,答案为联通块个数-1.水,可是我跳坑了. 我一开始做法是设总的联通块有(n+m)个,然后没合并一次减去1.其实这样是不可行的,因为我们只需要考虑奶牛(即节点1..n)有几个联通块.有可能一些语言根本没有任何奶牛掌握-- 1 #in