/**
problem: http://poj.org/problem?id=3177
tarjan blog: https://blog.csdn.net/reverie_mjp/article/details/51704523 v 为下一结点, u为当前结点
如果low[v] > dfn[u] 则 边(u,v)为桥
缩点后剩下的所有边都为桥(缩点后即为树结构)
将叶子结点相连使其成为双联通分量为最优解
所以:
添加(leaf + 1) / 2 条边即可使图成为双联通图
**/
#include<stdio.h>
#include<stack>
#include<algorithm>
using namespace std; class Graphics{
const static int MAXN = ;
const static int MAXM = * ;
private:
struct Edge{
int to, next;
bool bridge;
}edge[MAXM];
struct Point{
int dfn, low, color;
}point[MAXN];
int first[MAXN], sign, sumOfPoint, dfnNum, colorNum;
bool vis[MAXN];
stack<int> stk;
void tarjan(int u, int preEdge = -){
point[u].low = dfnNum;
point[u].dfn = dfnNum ++;
vis[u] = true;
stk.push(u);
for(int i = first[u]; i != -; i = edge[i].next){
int to = edge[i].to;
if((i^) == preEdge) continue; /// 由于是双向边,防止由该边跑回原来的点
if(!point[to].dfn){
tarjan(to, i);
point[u].low = min(point[u].low, point[to].low);
if(point[to].low > point[u].dfn){
edge[i].bridge = true;
edge[i^].bridge = true;
}
}else if(vis[to]){
point[u].low = min(point[to].dfn, point[u].low);
}
}
if(point[u].dfn == point[u].low){
vis[u] = false;
point[u].color = ++ colorNum;
while(stk.top() != u){
point[stk.top()].color = colorNum;
vis[stk.top()] = false;
stk.pop();
}
stk.pop();
}
}
public:
void init(int n){
sumOfPoint = n;
for(int i = ; i <= n; i ++){
first[i] = -;
vis[i] = ;
}
sign = colorNum = ;
dfnNum = ;
}
void addEdgeOneWay(int u, int v){
edge[sign].to = v;
edge[sign].next = first[u];
edge[sign].bridge = false;
first[u] = sign ++;
}
void addEdgeTwoWay(int u, int v){
addEdgeOneWay(u, v);
addEdgeOneWay(v, u);
}
void tarjanAllPoint(){
for(int i = ; i <= sumOfPoint; i ++){
if(!point[i].dfn)
tarjan(i);
}
}
int getAns(){
int *degree = new int[sumOfPoint+];
int ans = ;
for(int i = ; i <= sumOfPoint; i ++){
degree[i] = ;
}
tarjanAllPoint();
for(int i = ; i <= sumOfPoint; i ++){
for(int j = first[i]; j != -; j = edge[j].next){
int to = edge[j].to;
if(edge[j].bridge){
degree[point[to].color] ++;
}
}
}
for(int i = ; i <= sumOfPoint; i ++){
if(degree[i] == ){
ans ++;
}
}
delete []degree; return (ans + ) / ;
}
}graph; int main(){
int f, r;
scanf("%d%d", &f, &r);
graph.init(f);
while(r --){
int a, b;
scanf("%d%d", &a, &b);
graph.addEdgeTwoWay(a, b);
}
printf("%d\n", graph.getAns());
return ;
}

ps:

防止由该边跑回原来的点不能判断(点)而要判断(边)即

这么写是有bug的
例如:重边

最新文章

  1. 【JAVA并发编程实战】4、CountDownLatch
  2. omnet++5.0安装使用
  3. linux 清除驱动对网卡的记录
  4. 如何使Android应用开机时自动启动
  5. 如何做好一名DBA【转】
  6. 格式化分区,报/dev/sdb1 is apparently in use by the system; will not make a filesystem here!
  7. #define XXX do{ XXX } while(0) 为什么使用
  8. [Hadoop] - TaskTracker源码分析
  9. readelf相关命令
  10. 【转】PC架构系列:CPU/RAM/IO总线的发展历史!
  11. poj 2104 主席树(区间第k大)
  12. overture不同行的音符应该如何连线?
  13. python笔记33-python3连mysql增删改查
  14. e790. 设置JSpinner的边框
  15. shell 脚本 随机抽取班上学生
  16. Wide-range regulator delivers 12V, 3A output from 16 to 100V source
  17. 浅谈PHP面向对象编程(三、构造方法和析构方法)
  18. JavaScript实现排序算法总结
  19. Markdown之表格的处理
  20. zookeeper(四):核心原理(Watcher、事件和状态)

热门文章

  1. HashMap put、get方法源码分析
  2. js之闭包
  3. js之变量介绍
  4. 前端(三大框架、Bootstrap,jQuery,自整理)
  5. javascript统计一个字符在一段字符串出现次数
  6. JsonConvert序列化问题
  7. 【Leetcode】【Easy】Valid Palindrome
  8. 【Spring实战】—— 13 AspectJ注解切面
  9. IOS Block的本质
  10. chpasswd