题目链接:HDU  4635

题目大意:

给你一个有向图,加有向边,使得这个图是简单有向图。问你最多加多少条有向边。

简单有向图:

1、不存在有向重边。

2、不存在图循环。(注意是不存在 “图” 循环,就是不能使整个图成为 “强连通图” 。意思是可以存在环,但不能是全图循环。同样,两个点之间可以有两条相反有向边。)

分析:

1、如果我要加最多的边,全图仍然不为 “强连通图” 。那么最多的情况就是,有两个巨大的环,他们之前有且仅有一条有向边。故先进行 “有向图缩点” ,先从 小环 开始分析。

2、加边加到最后,一定存在仅剩的两个超级点 X 与 Y ,且 X 与 Y 之间有且仅有一条有向边。这样可以使得 X Y 分处两个最大环。

3、缩点加边到最后,X 与 Y 一定是 X → Y 或者 Y → X 的,所以作为 X Y 的前提条件是, 入度为 0 或者出度为 0 。(重点)

4、其次,X 与 Y 是两个最大有向环,那么我们可以使 X 或 Y 变成完全图,就可以继续加边而且不会导致全图变成 “强连通图” ,因为 X 与 Y 中间始终仅有一条有向边。

5、假设 X Y 之间有 : X → Y ,则我使 X 中的所有节点 ,全部以 → 有向边连接 Y 中的所有节点,也不会使得全图变为 “强连通图” ,故我还可以这样加边。(注意,连的边一定要与 X 到 Y 之间的有向边同向,否则就变成环了)

通过以上分析我们可以知道思路:

假设 X 的节点数为 x ,Y 的节点数为 y 。

1、以 X 为完全图时,X 中的有向边数最多为: x * (x - 1)

2、以 Y 为完全图时,Y 中的有向边数最多为: y * (y - 1)

3、X 中的全部节点以同一种有向边连接 Y 的全部节点,边数: x * y

4、由于给了 m 条边,故只需要加 x * (x - 1)+ y * (y - 1)+ x * y - m 条边即可。

将上面的 x * (x - 1)+ y * (y - 1)+ x * y - m x + y = n 联立得:

加的边数为:n2 - x * y - n - m

故我们只需要使 x * y 最小即可。而由于 x + y = n ,是定值,所以 x * y 的最小值即 x 的最小值 乘以 y (y = n - x) 。

由于 X 与 Y 是入度或出度为 0 的点,故只要找出这类缩点后的超级点中,点的个数最小的作为 X 点,X 自身成为完全图,不需要加别的点。然后剩下的所有点与 Y 点一同成为完全图即可。这要就可以保证 x 最小了。

代码如下:

#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#define maxn 100008
using namespace std;
typedef long long ll;
int cnt, Index, tot, sum;
int head[maxn], low[maxn], dfn[maxn], q[maxn];
int in[maxn],out[maxn];
int qhead[maxn];
bool vis[maxn];
int pre[maxn];
ll ans[maxn],n,m;
struct Edge
{
int to;
int next;
}edge[maxn << ];
inline void add(int u, int v)
{
edge[++cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt;
return;
}
inline void tarjan(int u)
{
low[u] = dfn[u] = ++Index;
q[++tot] = u;
vis[u] = true;
for (int i = head[u]; i; i = edge[i].next)
{
int v = edge[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (vis[v]) low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u])
{
++sum;
do {
ans[sum]++;
pre[q[tot]] = sum;
vis[q[tot--]] = false;
} while (q[tot + ] != u);
}
return;
}
void init()
{
cnt=Index=tot=sum=;
for(int i=;i<=n;i++) dfn[i]=vis[i]=head[i]=qhead[i]=ans[i]=in[i]=out[i]=;
memset(edge,,sizeof(edge));
return;
}
int main()
{
int t;
scanf("%d",&t);
int T=;
while(t--){
init();
scanf("%lld%lld", &n, &m);
int A, B;
for (int i = ; i <= m; i++) {
scanf("%d%d", &A, &B);
add(A, B);
}
for (int i = ; i <= n; i++) {
if (!dfn[i]) tarjan(i);
}
if(sum==){ printf("Case %d: -1\n",++T ); continue;}
cnt = ;
for (int i = ; i <= n; i++) {
for (int j = head[i]; j; j = edge[j].next) {
int v = edge[j].to;
if (pre[i] != pre[v]){
in[pre[v]]++,out[pre[i]]++;
}
}
}
ll res=0x3f3f3f3f;
for(int i=;i<=sum;i++){
if(in[i]==||out[i]==){
res=min(res,ans[i]);
}
}
res=1ll*n*n-n-m-res*(n-res);
printf("Case %d: %lld\n",++T,res );
}
}

HDU 4635 (完全图 和 有向图缩点)的更多相关文章

  1. hdu 4635 Strongly connected 强连通缩点

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4635 题意:给你一个n个点m条边的图,问在图不是强连通图的情况下,最多可以向图中添多少条边,若图为原来 ...

  2. Strongly connected HDU - 4635(判断强连通图 缩点)

    找出强联通块,计算每个连通块内的点数.将点数最少的那个连通块单独拿出来,其余的连通块合并成一个连通分量. 那么假设第一个连通块的 点数是 x  第二个连通块的点数是 y 一个强连通图(每两个点之间,至 ...

  3. hdu 3072 有向图缩点成最小树形图计算最小权

    题意,从0点出发,遍历所有点,遍历边时候要付出代价,在一个SCC中的边不要付费.求最小费用. 有向图缩点(无需建立新图,,n<=50000,建则超时),遍历边,若不在一个SCC中,用一个数组更新 ...

  4. hdu 1827 有向图缩点看度数

    题意:给一个有向图,选最少的点(同时最小价值),从这些点出发可以遍历所有. 思路:先有向图缩点,成有向树,找入度为0的点即可. 下面给出有向图缩点方法: 用一个数组SCC记录即可,重新编号,1.... ...

  5. HDU 4635 —— Strongly connected——————【 强连通、最多加多少边仍不强连通】

    Strongly connected Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u ...

  6. HDU 4635 Strongly connected (强连通分量)

    题意 给定一个N个点M条边的简单图,求最多能加几条边,使得这个图仍然不是一个强连通图. 思路 2013多校第四场1004题.和官方题解思路一样,就直接贴了~ 最终添加完边的图,肯定可以分成两个部X和Y ...

  7. HDU 4635 - Strongly connected(2013MUTC4-1004)(强连通分量)

    t这道题在我们队属于我的范畴,最终因为最后一个环节想错了,也没搞出来 题解是这么说的: 最终添加完边的图,肯定可以分成两个部X和Y,其中只有X到Y的边没有Y到X的边,那么要使得边数尽可能的多,则X部肯 ...

  8. hdu 4635 Strongly connected(Tarjan)

    做完后,看了解题报告,思路是一样的.我就直接粘过来吧 最终添加完边的图,肯定可以分成两个部X和Y,其中只有X到Y的边没有Y到X的边,那么要使得边数尽可能的多,则X部肯定是一个完全图,Y部也是,同时X部 ...

  9. HDU 4635 Strongly connected (Tarjan+一点数学分析)

    Strongly connected Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) ...

随机推荐

  1. 究竟什么是Windows句柄

    图解说明——究竟什么是Windows句柄     这里需要说明: 1.这里将句柄所能标识的所有东西(如窗口.文件.画笔等)统称为“对象”. 2.图中一个小横框表示一定大小的内存区域,并不代表一个字节, ...

  2. Vue快速学习_第二节

    表单输入绑定(v-model) v-model 指令在表单 <input>.<textarea> 及 <select> 元素上创建双向数据绑定(注意只在表单这几个可 ...

  3. 音频编辑器 OcenAudio v3.1.9.0 绿色便携版

    下载地址:点我 基本介绍 ocenaudio是一款跨平台的,易于使用的,快速的,功能强大的,好用的音频编辑软件.该软件支持Virtual Studio Technology插件,美观.统一的跨平台界面 ...

  4. Flutter学习笔记(8)--Dart面向对象

    如需转载,请注明出处:Flutter学习笔记(7)--Dart异常处理 Dart作为高级语言,支持面向对象的很多特性,并且支持基于mixin的继承方式,基于mixin的继承方式是指:一个类可以继承自多 ...

  5. Kafka部署

    Kafka依赖Zookeeper,虽然Kafka自带zookeeper,但是建议单独部署,所以先部署Zookeeper. 测试环境 citus1,citus2,citus3三台机器.对主机名和ip在/ ...

  6. tomcat不需要重启热部署xml文件

    项目中,遇到情况,有时候增加struts的配置了,有时候粗心改错了,然后急需要发布线上吧,又不能重启影响其他的,最后发现struts有这个功能呢! 在struts.xml的配置文件中加上一句话就行 & ...

  7. Leetcode solution 291: Word Pattern II

    Problem Statement Given a pattern and a string str, find if str follows the same pattern. Here follo ...

  8. Kafka配置信息

    Kafka配置信息 broker配置信息 属性 默认值 描述 broker.id 必填参数,broker的唯一标识 log.dirs /tmp/kafka-logs Kafka数据存放的目录.可以指定 ...

  9. 《VR入门系列教程》之20---使用Oculus移动端SDK

    使用Oculus移动端SDK     在基于安卓系统的GearVR上开发应用需要用到Oculus的移动端SDK,下面的网址可以下载SDK:http://developer.oculus.com     ...

  10. hive show databases 添加条件

    show databases like 'test012301' ; 通配符: show databases like 'a*';