ps.有关Tarjan算法缩点的简要回顾。

今天上午在Luogu随机到了这道题 Luogu P3420,题目大概是这样:

题目描述

Byteazar the Dragon拥有N个小猪存钱罐。每一个存钱罐能够用相应的钥匙打开或者被砸开。Byteazar已经将钥匙放入到一些存钱罐中。现在已知每个钥匙所在的存钱罐,Byteazar想要买一辆小汽车,而且需要打开所有的存钱罐。然而,他想要破坏尽量少的存钱罐,帮助Byteazar去决策最少要破坏多少存钱罐。

你需要写一段程序包括:读入存钱罐的数量以及相应的钥匙的位置,求出能打开所有存钱罐的情况下,需要破坏的存钱罐的最少数量并将其输出。

输入输出格式

输入格式:

第一行:包括一个整数N(1<=N<=1000000),这是Byteazar the Dragon拥有的存钱罐的数量。

存钱罐(包括它们对应的钥匙)从1到N编号。

接下来有N行:第i+1行包括一个整数x,表示第i个存钱罐对应的钥匙放置在了第x个存钱罐中。

输出格式:

仅一行:包括一个整数,表示能打开所有存钱罐的情况下,需要破坏的存钱罐的最少数量。

输入输出样例

输入样例#1:

4
2
1
2
4
输出样例#1:

2

看到这道题的时候我是很高兴的:这不就是记录一个入度的问题嘛!图都不需要建就做好了。于是写了一个前后不超20行的代码就交了,果断拿了20分。

然后我开始重新审视这道题目:我发现题目是有环的,而且还可能有好多个,所以入度为零的点可能一个都没有,而且还有各种问题……

糟了,难度一下就上来了。不过好在我会复制敲Tarjan的板子!可以发现,只要砸开(打开)一个环里的任一个存钱罐,那么整个环就都会被打开。

缩点!

Tarjan的本质其实就是Dfs的过程,而缩点的思想是,我们将未搜过的点称为白点,已退栈(已经Dfs过)的点称为黑点,而正在Dfs栈中的点定义为灰点。

然后我们可以愉快地Dfs,遇到白点就走,黑点就返回(因为已经搜完了)。

但是如果搜着搜着搜到了灰点,也就是vis[x] = 1,dfn[x] < dfn[now]的点,那么说明这玩意儿转回去了……那这就是一个环。

这时我们对每个节点维护的2个变量——dfn(被dfs到的次序,也叫时间戳),low(标记了节点i能够回溯到的最早位于栈中的节点,也就是你走回去到达的节点啦)其中的low进行操作取min,这里具体的原理和作用我并说不很清楚,背过理解就好。

之后就弹栈,将这一段环染成同一种颜色,也就是缩点。

 int stack[maxn], top;

 void Tarjan(int s) {
dfn[s] = low[s] = ++dfn_num;
vis[s] = , stack[++top] = s;
for(int i=head[s]; i; i=edge[i].nxt) {
int v = edge[i].v;
if( !dfn[v] ) {
Tarjan(v), low[s] = min(low[s], low[v]);
} else if( vis[v] ) low[s] = min(low[s], dfn[v]);
}
if( dfn[s]==low[s] ) {
vis[s] = , col[s] = ++col_num;
while( stack[top]!=s ) {
col[stack[top]] = col_num;
vis[stack[top]] = , --top;
}
--top;
}
}

Tarjan缩点

不一会我就复制来了敲完了板子,高高兴兴的提交了上去——40分。

???

其实这篇博客的重点已经写完了,不过我还要继续写。

我只好仔细对着题解检查然后调试,发现我边建反了……

而且Tarjan判断一个点已经被搜过的根据是dfn[] != 0,不是vis[],vis[]只是一个是否在栈中的标记,也就是给灰点的标记……而我在main()里却拿vis[]来判断是否搜过这个点了。

以及在对新图做什么的时候,注意此时的点已经不是一开始的点了,而是一个颜色代表一个点。所以操作的时候大多是应该是对col[n]操作,而不是n……

然后再统计一下入度为0的点就好啦。

最后帖一波代码。

 #include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std; const int maxn = + ;
int n, t, head[maxn], vis[maxn], degree[maxn], ans;
int dfn[maxn], low[maxn], col[maxn], dfn_num, col_num; struct Edge { int v, nxt; }edge[maxn]; inline void read(int &x) {
register char ch = ; x = ;
while( !isdigit(ch) ) ch = getchar();
while( isdigit(ch) ) x = (x*) + (ch^), ch = getchar();
} int stack[maxn], top; void Tarjan(int s) {
dfn[s] = low[s] = ++dfn_num;
vis[s] = , stack[++top] = s;
for(int i=head[s]; i; i=edge[i].nxt) {
int v = edge[i].v;
if( !dfn[v] ) {
Tarjan(v), low[s] = min(low[s], low[v]);
} else if( vis[v] ) low[s] = min(low[s], dfn[v]);
}
if( dfn[s]==low[s] ) {
vis[s] = , col[s] = ++col_num;
while( stack[top]!=s ) {
col[stack[top]] = col_num;
vis[stack[top]] = , --top;
}
--top;
}
} int main(int argc, char const *argv[])
{
scanf("%d", &n);
for(int i=; i<=n; ++i) {
read(t), edge[i].v = i;
edge[i].nxt = head[t], head[t] = i;
}
for(int i=; i<=n; ++i) if( !col[i] ) Tarjan(i);
for(int i=; i<=n; ++i) {
for(int j=head[i]; j; j=edge[j].nxt)
if( col[edge[j].v]!=col[i] ) ++degree[col[edge[j].v]];
}
for(int i=; i<=col_num; ++i) if( !degree[i] ) ++ans;
printf("%d\n", ans);
return ;
}

[POI2005] SKA-Piggy Banks的更多相关文章

  1. [BZOJ1529][POI2005]ska Piggy banks

    [BZOJ1529][POI2005]ska Piggy banks 试题描述 Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放 ...

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

    每一连通块砸开一个就可以拿到所有的钱, 所以用并查集求连通块数 ------------------------------------------------------------------- ...

  3. 【BZOJ1529】[POI2005]ska Piggy banks Tarjan

    [BZOJ1529][POI2005]ska Piggy banks Description Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个 ...

  4. bzoj1529 [POI2005]ska Piggy banks 并查集

    [POI2005]ska Piggy banks Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1508  Solved: 763[Submit][Sta ...

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

    [bzoj1529][POI2005]ska Piggy banks Description Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个 ...

  6. 【BZOJ】1529 [POI2005]ska Piggy banks

    [算法](强连通分量)并查集 [题解] 1.用tarjan计算强连通分量并缩点,在新图中找入度为0的点的个数就是答案. 但是,会爆内存(题目内存限制64MB). 2.用并查集,最后从1到n统计fa[i ...

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

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1529 [题目大意] 给出一张n个点n条边的有向图,问选取几个点为起点可以遍历全图 [题 ...

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

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1529 题意: Byteazar有N个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. By ...

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

    本来以为是tarjan缩点……但是64MB的空间根本不足以存下原图和缩点后的新图.所以呢……并查集= = orz hzwer MLE的tarjan: /************************ ...

  10. [Poi2005]Piggy Banks小猪存钱罐

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

随机推荐

  1. 转载 --mysql函数大全

    控制流函数 IFNULL(expr1,expr2) 如果expr1不是NULL,IFNULL()返回expr1,否则它返回expr2.IFNULL()返回一个数字或字符串值,取决于它被使用的上下文环境 ...

  2. vuex2.0 基本使用(2) --- mutation 和 action

    我们的项目非常简单,当点击+1按钮的时候,count 加1,点击-1按钮的时候,count 减1. 1, mutation The only way to actually change state ...

  3. css溢出显示省略号

    单行溢出省略号 .show-detail li .info-name { width:278px; display:inline-block; /*下面是重点*/ overflow: hidden; ...

  4. 基准对象object中的基础类型----数字 (二)

    object有如下子类: CLASSES object basestring str unicode buffer bytearray classmethod complex dict enumera ...

  5. BZOJ1001 洛谷4001 [BJOI2006]狼抓兔子 题解

    题目 这个题目有多种解法,这个题也是一个比较经典的题了,正是因为他的多样的做法,这个题主要难在建图和优化,因为这是一个网格图,所以spfa肯定过不去,所以用最短路解法的话,只能用dij,而网络流也是要 ...

  6. 【BZOJ3809】Gty的二逼妹子序列 莫队 分块

    题目描述 给你一个长度为\(n\)的数列,还有\(m\)个询问,对于每个询问\((l,r,a,b)\),输出区间\([l,r]\)有多少范围在\([a,b]\)的权值. \(n\leq 100000, ...

  7. bzoj4671: 异或图

    bzoj4671: 异或图 Description 定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G, 其中如果 (u, v) 在 G1 与 G2 中的出现次数之和为 1, 那么边 ( ...

  8. IDEA+Springboot+JRebel热部署实现

    步骤一:在IDEA中安装JRebel插件(File->settings->plugins->search in repositories),如下图 步骤二:安装完成之后,重启idea ...

  9. 线段树 by yyb

    线段树 by yyb Type1 维护特殊信息 1.[洛谷1438]无聊的数列 维护一个数列,两种操作 1.给一段区间加上一个等差数列 2.单点询问值 维护等差数列 不难发现,等差数列可以写成\(ad ...

  10. 【BZOJ3252】攻略(长链剖分,贪心)

    [BZOJ3252]攻略(长链剖分,贪心) 题面 BZOJ 给定一棵树,每个点有点权,选定\(k\)个叶子,满足根到\(k\)个叶子的所有路径所覆盖的点权和最大. 题解 一个假装是对的贪心: 每次选择 ...