[Poi2005]Piggy Banks小猪存钱罐

题目描述

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

输入格式

第一行一个整数 N (1 <= N <= 1.000.000)表示存钱罐的总数.

接下来每行一个整数,第 i+1行的整数代表第i个存钱罐的钥匙放置的存钱罐编号.

输出格式

一个整数表示最少打破多少个存钱罐.


分析题目性质。

题目给了若干个单向关系,我们可以依照这个建出一张有向图。那么显然,只要我们有了一个存钱罐的钥匙,那这个强连通分量里的所有存钱罐都可以打开了。那么我们对图中所有强连通分量进行缩点,入度>0的点就通过其它点得到钥匙,入度为0的点我们就砸开它。所以答案就是入度为0的强连通分量个数。

强连通分量用Tarjan来求,时间复杂度为O(N)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 1000001
using namespace std;

vector<int> to[maxn];
int dfn[maxn],low[maxn],tot;
int col[maxn],ind[maxn],cnt;
int stack[maxn],top;
bool instack[maxn];
int n,ans;

inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}

void tarjan(int u){
    dfn[u]=low[u]=++tot,stack[++top]=u,instack[u]=true;
    for(register int i=0;i<to[u].size();i++){
        int v=to[u][i];
        if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
        else if(instack[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        int v; cnt++;
        do{ v=stack[top--],instack[v]=false; col[v]=cnt; }while(v!=u);
    }
}

int main(){
    n=read();
    for(register int i=1;i<=n;i++) to[read()].push_back(i);
    for(register int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(register int i=1;i<=n;i++){
        for(register int j=0;j<to[i].size();j++){
            int v=to[i][j];
            if(col[i]!=col[v]) ind[col[v]]++;
        }
    }
    for(register int i=1;i<=cnt;i++) if(!ind[i]) ans++;
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/akura/p/10957165.html

时间: 2024-10-08 08:34:51

[Poi2005]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 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,

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)

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

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

Sicily 1350. Piggy banks 解题报告

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

[POI2005] SKA-Piggy Banks

ps.有关Tarjan算法缩点的简要回顾. 今天上午在Luogu随机到了这道题 Luogu P3420,题目大概是这样: 题目描述 Byteazar the Dragon拥有N个小猪存钱罐.每一个存钱罐能够用相应的钥匙打开或者被砸开.Byteazar已经将钥匙放入到一些存钱罐中.现在已知每个钥匙所在的存钱罐,Byteazar想要买一辆小汽车,而且需要打开所有的存钱罐.然而,他想要破坏尽量少的存钱罐,帮助Byteazar去决策最少要破坏多少存钱罐. 你需要写一段程序包括:读入存钱罐的数量以及相应的

【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 [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(并查集)

[题目链接] 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 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