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;
}
posted @ 2018-10-09 18:17  _王小呆  阅读(171)  评论(0编辑  收藏  举报