[POI2005] SKA-Piggy Banks
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个存钱罐中。
输出格式:
仅一行:包括一个整数,表示能打开所有存钱罐的情况下,需要破坏的存钱罐的最少数量。
输入输出样例
4
2
1
2
4
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的更多相关文章
- [BZOJ1529][POI2005]ska Piggy banks
[BZOJ1529][POI2005]ska Piggy banks 试题描述 Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放 ...
- BZOJ 1529: [POI2005]ska Piggy banks( 并查集 )
每一连通块砸开一个就可以拿到所有的钱, 所以用并查集求连通块数 ------------------------------------------------------------------- ...
- 【BZOJ1529】[POI2005]ska Piggy banks Tarjan
[BZOJ1529][POI2005]ska Piggy banks Description Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个 ...
- bzoj1529 [POI2005]ska Piggy banks 并查集
[POI2005]ska Piggy banks Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 1508 Solved: 763[Submit][Sta ...
- Taran 缩点【bzoj1529】[POI2005]ska Piggy banks
[bzoj1529][POI2005]ska Piggy banks Description Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个 ...
- 【BZOJ】1529 [POI2005]ska Piggy banks
[算法](强连通分量)并查集 [题解] 1.用tarjan计算强连通分量并缩点,在新图中找入度为0的点的个数就是答案. 但是,会爆内存(题目内存限制64MB). 2.用并查集,最后从1到n统计fa[i ...
- BZOJ 1529 [POI2005]ska Piggy banks(并查集)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1529 [题目大意] 给出一张n个点n条边的有向图,问选取几个点为起点可以遍历全图 [题 ...
- BZOJ 1529 [POI2005]ska Piggy banks:并查集
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1529 题意: Byteazar有N个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. By ...
- 【BZOJ】【1529】 【POI2005】ska Piggy banks
本来以为是tarjan缩点……但是64MB的空间根本不足以存下原图和缩点后的新图.所以呢……并查集= = orz hzwer MLE的tarjan: /************************ ...
- [Poi2005]Piggy Banks小猪存钱罐
题目描述 Byteazar有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取 ...
随机推荐
- Unable to resolve target 'android-15'
SDK 15没有加载造成的,在SDK Manager.exe下安装以下文件 Android SDK Tools (25.2.5) Android SDK Platform-tools (28.0.1) ...
- codeforces-962-c
题意:给你一个数,问从中删除某几位数字后重新组成的数字是否是某个数的平方: 解题思路:数据小,dfs直接搜,每位数只有两种选择,要或者不要 #include<iostream> #incl ...
- PlaNet,使用图像输入来学习世界模型
Google AI团队与DeepMind合作,上周宣布了一个名为PlaNet的新的开源“Deep Planning”网络. PlaNet是一个人工智能代理,它只使用图像输入来学习世界模型,并使用这些模 ...
- HDU5769-Substring-多校#4-1006-后缀数组
给定一个字符x和一个字符串.要求输出包含此字符的所有不同字串. 后缀数组可以计算一个字符串的所有不同字串,理解了原理就能做这题了. 对于每一个后缀i,将产生len-sa[i]-hight[i]的前缀, ...
- Hibernate 注解映射
工作中遇到hibernate映射的一些问题,这里总结一下 (特别是测试时,许多数据并不能有效关联.所以@NotFound 很重要) 一,假设有2张表user,company 我们知道,一个用户属于一个 ...
- selenium+python启动Firefox浏览器失败问题和点击登陆按钮无效问题
问题1:使用python+selenium编写脚本调用Firefox时报错:
- EMM386和UMBPCI 区别
EMM386和UMBPCI 区别 1,SupportCD-ROM[HIMEM+EMM386NOEMS].支持光驱(EMM386模式)2,SupportCD-ROM[HIMEM+UMBPCI].支持光驱 ...
- MT【298】双参数非齐次
若函数$f(x)=x^2+(\dfrac{1}{3}+a)x+b$在$[-1,1]$上有零点,则$a^2-3b$的最小值为_____ 分析:设零点为$x_0$,则$b=-x^2_0-(\dfrac{1 ...
- 【cf849D】Rooter's Song(思维)
D. Rooter's Song 题意 x轴.y轴上有n个人,第i个人\(g_i==1\)则坐标为\((p_i,0)\)否则\((0,p_i)\),\(t_i\)秒后垂直所在坐标轴出发,到达边界x=w ...
- linux开发板出现Read-only file system的解决办法
@2018-11-29 创建文件夹出现如下提示 mkdir: can't create directory 'test': Read-only file system 使用命令 mount rw -o ...