@description@

有一天你学了一个叫能求出有向图中所有的强连通分量的算法,你决定将这个算法应用到NOI比赛中。

这是一道交互题。交互库有一张有向图,图中有 n 个点和 m 条有向边。为了与交互库交互,你可以进行如下操作:

给定一个点 x 和一些点构成的一个集合 S,交互库将会回答是否存在一条从点 x 到 S 中的任何一个点的有向边。

给定一个点 x 和一些点构成的一个集合 S,交互库将会回答是否存在一条从 S 中的任何一个点到点 x 的有向边。

你希望找出图中所有的强连通分量。两个操作的调用次数的和不能超过25000.

为了证明你的确找出了强连通分量,在所有操作结束之后你需要回答所有强连通分量大小的平方和。

任务描述:

你需要实现以下函数:

findSCC(n, m)

n, m 的意义见题目描述;

你需要返回一个整数 ans,表示所有强连通分量大小的平方和。

你可以调用以下几个函数:

vertexToSet(x, s)

x为点的编号,编号的下标从0开始;

s为一个大小为 n 的数组,描述集合S;

s[i]的值只有可能是0或者1。

如果s[i]的值为0,则说明编号为i的点不在SS中;

如果s[i]的值为1,则说明编号为i的点在SS中。

该函数返回值只有可能是0或者1,如果返回值为1则说明存在一条从点x到S中的任何一个点的有向边,否则不存在。

setToVertex(x, s)

x和s的意义和前一个函数类似,不再赘述。

该函数返回值只有可能是0或者1,如果返回值为1则说明存在一条从SS中的任何一个点到点xx的有向边,否则不存在。 一个点的有向边,否则不存在。

为了保证函数能正常返回结果,你的s数组必须至少有n的大小。保证s中下标大于n的元素不会被访问。此外,两个函数都不会对s数组进行修改。

不合法的参数将导致未定义行为,后果自负。

实现细节:

本题只支持 C++。你需要包含头文件scc.h。

你需要实现的函数:

int findSCC(int n, int m);

所有你可以调用的函数的接口信息如下:

int vertexToSet(int x, int s[]);

int setToVertex(int x, int s[]);

评测方式:

评测系统将读入如下格式的输入数据:

第 1 行: n,m

接下来 m 行,每行两个整数xi,yi, 表示存在一条从编号为xi的点到编号为yi的点的有向边。

注意图中可能有重边和自环。

在 findSCC 返回后,评测系统将输出你的答案以及vertexToSet,setToVertex两个操作的调用次数的和。

样例一

input

6 5

0 1

1 2

2 0

3 4

4 5

output

12

explanation

前三个点构成一个强连通分量,剩下三个点单独构成自己的强连通分量,强连通分量大小的平方和为32+12+12+12=12。

对于所有测试点,1≤n≤1000,1≤m≤100000。

@solution@

操作总次数:25000。n 的上界:1000。

两者相除等于多少?25。可以发现这个数字与 log(n) 是同阶的。

这启发我们或许可以用什么 log 的算法解决问题。

考虑给定一个集合 S 与点 x,并且已知 x 与 S 有边相连。

我们将 S 划分成两个等大的集合 S1 与 S2,判断 S1 与 S2 哪一个集合与 x 有边相连,然后进行分治。

这样分治到最后,我们就得到了一个 x 在 S 中有边相连的点。

即:我们可以在 log 的次数在 S 中找到一个与 x 有边相连的点。

于是我们自然也可以在 n*log 的次数对整个图进行一次遍历。

考虑求解强连通分量的算法:tarjan 与 kosaraju 算法。

因为 tarjan 涉及到要找回边,按照我上述的算法是不能找到这样一条边的,所以考场上我就没写 tarjan。

但实际上根据标算的说法, tarjan 是可行的。此处暂不提。

考虑 kosaraju 的流程:随便选一个点进行 dfs 的遍历,记录每个点出 dfs 的时间戳,按时间戳从大到小在逆图中 dfs。

dfs 可以参照我上述的算法。但是我们无法对原图修改,怎么做到逆图呢?

注意到题目中的询问分为两类,一类是集合到点的连通,一类是点到集合的连通。正着时取点到集合,逆着时取集合到点即可。

@accepted code@

#include "scc.h"
#include<vector>
#include<cstdio>
using namespace std;
const int MAXN = 1000;
int s[MAXN + 5], dfn[MAXN + 5], tmp[MAXN + 5];
int n, dcnt, siz;
int find_edge(int x, vector<int>v, int type) {
if( v.size() == 1 ) return v[0];
vector<int>vl, vr;
int mid = v.size()/2;
for(int i=0;i<mid;i++)
vl.push_back(v[i]);
for(int i=mid;i<v.size();i++)
vr.push_back(v[i]);
for(int i=0;i<vl.size();i++)
tmp[v[i]] = 1;
bool flag = (type ? setToVertex(x, tmp) : vertexToSet(x, tmp));
for(int i=0;i<vl.size();i++)
tmp[v[i]] = 0;
return flag ? find_edge(x, vl, type) : find_edge(x, vr, type);
}
int find_edge(int x, int type) {
vector<int>vec;
for(int i=0;i<n;i++)
if( s[i] ) vec.push_back(i);
return find_edge(x, vec, type);
}
void dfs1(int x) {
s[x] = 0;
while( vertexToSet(x, s) )
dfs1(find_edge(x, 0));
dfn[dcnt++] = x;
}
void dfs2(int x) {
s[x] = 0, siz++;
while( setToVertex(x, s) )
dfs2(find_edge(x, 1));
}
int findSCC(int _n, int m) {
n = _n;
for(int i=0;i<n;i++) s[i] = 1, dfn[i] = 0;
dcnt = 0;
for(int i=0;i<n;i++)
if( s[i] == 1 ) dfs1(i);
for(int i=0;i<n;i++) s[i] = 1;
int ret = 0;
for(int i=n-1;i>=0;i--)
if( s[dfn[i]] == 1 ) siz = 0, dfs2(dfn[i]), ret += siz*siz;
return ret;
}

@details@

其实难度不大。。。一道签到题?算是吧。(毕竟我都写得起正解)

不过遇到交互题还是一件挺让人兴奋的事的。

上一次做是在冬令营时吧。。。啊啊,时光真快。

好像跑题了。反正这道题几乎就是一个强连通的模板啦。

@noi.ac - 506@ 强连通分量的更多相关文章

  1. ZOJ3784 String of Infinity(AC自动机&&强连通分量)

    题意:给你n个禁止串,然后你只能用字符表的前m个字符去写一个无限长的串,要求是不能包含禁止串,而且串在后面不能出现循环 比赛的时候想的是先建一个自动机,然后将自动机确定化,不能到达的状态全部弄出来.但 ...

  2. PAT 甲级 1013 Battle Over Cities (25 分)(图的遍历,统计强连通分量个数,bfs,一遍就ac啦)

    1013 Battle Over Cities (25 分)   It is vitally important to have all the cities connected by highway ...

  3. 图论之tarjan真乃神人也,强连通分量,割点,桥,双连通他都会

    先来%一下Robert Tarjan前辈 %%%%%%%%%%%%%%%%%% 然后是热情感谢下列并不止这些大佬的博客: 图连通性(一):Tarjan算法求解有向图强连通分量 图连通性(二):Tarj ...

  4. poj2186Popular Cows(Kosaraju算法--有向图的强连通分量的分解)

    /* 题目大意:有N个cows, M个关系 a->b 表示 a认为b popular:如果还有b->c, 那么就会有a->c 问最终有多少个cows被其他所有cows认为是popul ...

  5. POJ2186 Popular Cows 强连通分量tarjan

    做这题主要是为了学习一下tarjan的强连通分量,因为包括桥,双连通分量,强连通分量很多的求法其实都可以源于tarjan的这种方法,通过一个low,pre数组求出来. 题意:给你许多的A->B ...

  6. HDU 1269 迷宫城堡 (强连通分量,常规)

    题意: 判断所给的有向图是否是一个强连通图. 思路: 如果连通分量大于1则必定No,如果强连通分量大于1也是No.tarjan算法求强连通分量. #include <cstdio> #in ...

  7. UVA 1324 The Largest Clique 最大团(强连通分量,变形)

    题意:给一个有向图,要求找出一些点,使得这些点中的任意点对,要么可以互通,要么单向可达. 思路:最低只要求单向可达即可,即a->b都可以算进去. 强连通分量内的点肯定是满足要求的,可以全选,但是 ...

  8. HDU 4635 Strongly connected(强连通分量,变形)

    题意:给出一个有向图(不一定连通),问最多可添加多少条边而该图仍然没有强连通. 思路: 强连通分量必须先求出,每个强连通分量包含有几个点也需要知道,每个点只会属于1个强连通分量. 在使图不强连通的前提 ...

  9. UVALive Proving Equivalences (强连通分量,常规)

    题意: 给一个有向图,问添加几条边可以使其强连通. 思路: tarjan算法求强连通分量,然后缩点求各个强连通分量的出入度,答案是max(入度为0的缩点个数,出度为0的缩点个数). #include ...

随机推荐

  1. Python3数据分析与挖掘建模实战

    Python3数据分析与挖掘建模实战  整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的时 ...

  2. Hdu 1156

    题目链接 Brownie Points II Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java ...

  3. Java 1.8 Stream 用例测试

    package stream; import model.Student; import org.junit.jupiter.api.Test; import java.util.*; import ...

  4. tomcat的日志不输出日志信息的解决方法

    1.下载日志jar包,例如:commons-logging-1.1.1.jar.放在tomcat的bin目录下,或者是自己项目的lib包里. 2.修改tomcat的bin目录下面的catalina.b ...

  5. 2018-5-22-SublimeText-粘贴图片保存到本地

    title author date CreateTime categories SublimeText 粘贴图片保存到本地 lindexi 2018-05-22 15:15:26 +0800 2018 ...

  6. SQLServer → 09:索引

    一.索引概念 用途 我们对数据查询及处理速度已成为衡量应用系统成败的标准,而采用索引来加快数据处理速度通常是最普遍采用的优化方法. 概念 索引是一个单独的,存储在磁盘上的数据结构,它们包含则对数据表里 ...

  7. 用五种不同的布局方式实现“左右300px中间自适应”的效果

    float浮动 <section class="layout float"> <style media="screen"> .layou ...

  8. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十章:混合

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十章:混合 代码工程地址: https://github.com/j ...

  9. 【Linux】vim缩进配置 (转载)

    1.安装vim,终端输入: wanpeng@ubuntu:~$ sudo apt-get install vim-full 2.终端输入,打开配置文件: wanpeng@ubuntu:~$ vim ~ ...

  10. Gym - 101617D_Jumping Haybales(BFS)

    Sample Input 4 2 .### #... .#.. #.#. 3 1 .#. .#. .#. Sample Output 4 -1 题意:给一个n*n的图,每次最多能跳k个格子,只能向南( ...