@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. 凸优化 & 1概念

    ---恢复内容开始--- 放射集合 系数之和为1 相加仍然能在集合内,就是 纺射集合 子空间加一个常熟 就是纺射集合 , 例题2.1 一类特殊的线性方程组的解可以看作纺射 集合 纺射包 aff C 是 ...

  2. mysql8下载安装及配置

    mysql8下载和安装 一.下载 官网地址:https://dev.mysql.com/downloads/mysql/8.0.html 选择“downloads”-->"mysql ...

  3. LUOGU 2593 : [Zjoi2006] 超级麻将

    传送门 解题思路 直接爆搜全T..状态数太多了,所以我们考虑贪心+剪枝.贪心:先拿三个连着的,再拿四个一样的,再拿三个一样的,最后拿两个一样的这样的搜索顺序最优,两个的放最后是因为只要这样的一个,三个 ...

  4. 客户端用javascript填充Dropdownlist,服务器端获取不到Dropdownlist的值

    今天遇到一个奇怪的问题,某一页面需要使用三级级联下拉列表框.为提高用户体验,采用jQuery的cascadingDropDown插件调用后台Web Services来实现ajax填充. 填充没有任何问 ...

  5. 你真的了解HTML吗

    有这么一段HTML,请挑毛病: <P> 哥写的不是HTML,是寂寞.<br><br> 我说: <br>不要迷恋哥,哥只是一个传说 这是原来雅虎一道笔试题 ...

  6. swift 风骚的Guard语法

    http://blog.csdn.net/pjk1129/article/details/48627153#0-qzone-1-64255-d020d2d2a4e8d1a374a433f596ad14 ...

  7. 友盟iOS sdk整理

    文档中心 :http://dev.umeng.com 集成文档:http://dev.umeng.com/analytics/ios-doc/integration 报表中心:http://www.u ...

  8. 如何用好消息推送(push)做APP运营

    作为移动端APP产品运营最重要的运营手段,消息推送(push)被越来越多的APP厂商所重视,在信息泛滥的移动互联网时代,手机APP应用安装得越来越多,小小的手机屏幕每天收到的消息推送也越来越多,站在用 ...

  9. 用var 变量=函数名 方式调用函数时如何传值的问题

    通过:xmlhttp.onreadystatechange= function(){FuncName(param)};orxmlhttp.onreadystatechange= new Functio ...

  10. php中括号定义数组

    php5.3及之前的版本是不支持中括号定义数组的.5.4之后支持. 错误信息是,不识别“[”