Taran 缩点【bzoj1529】[POI2005]ska Piggy banks

【bzoj1529】[POI2005]ska Piggy banks

Description

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

Input

第一行一个整数 N (1 <= N <= 1.000.000) – 表示存钱罐的总数. 接下来每行一个整数,第 i+1行的整数代表第i个存钱罐的钥匙放置的存钱罐编号.

Output

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

Tarjan 很简单的题目。

但是很毒瘤地卡了空间。

首先,鉴于每个点只向外连一条有向边,所以不要用常规存边方式存边,直接一个数组就可以了。

并且在缩完点之后,统计每个强连通分量的出度,出度为零的强联通分量的个数即为答案。

我还整了个循环数组,卡空间是在是烦。把dfn直接当做vis用了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int wx=1000017;
inline int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0';ch=getchar();}
    return sum*f;
}

int n,x,col,top,ans,tot;
int num;
int st[wx],dfn[wx],low[wx],head[wx],belong[wx],a[wx];

void Tarjan(int u){
    dfn[u]=low[u]=++tot;
    st[++top]=u;
    if(!dfn[a[u]]){
        Tarjan(a[u]);
        low[u]=min(low[u],low[a[u]]);
    }
    else if(!belong[a[u]]){
        low[u]=min(low[u],dfn[a[u]]);
    }
    if(low[u]==dfn[u]){
        belong[u]=++col;
        while(st[top]!=u){
            belong[st[top]]=col;
            top--;
        }
        top--;
    }
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(i);
    memset(dfn,0,sizeof dfn);
    for(int i=1;i<=n;i++){
        if(belong[i]!=belong[a[i]])dfn[belong[i]]++;
    }
    for(int i=1;i<=col;i++)if(!dfn[i])ans++;
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wangxiaodai/p/9761973.html
今日推荐