uestc 方老师和农场

转自http://www.cnblogs.com/whatbeg/p/3765624.html

首先将原图中的连通分量缩点,一定可以将原图缩成一棵树的形式,然后统计这棵树的叶子节点个数,答案就是(leaf+1)/2。这里不再证明,可以画个图看一下。

(简单说明一下,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。  --Byvoid)

怎么统计呢?用并查集缩点,可以知道,缩点后留下的边全部是原图的桥,这是我们可以用Tarjan求出原图的所有桥,然后枚举每条桥,桥两端的点度数分别+1,就可以求出每个点(缩点后的点)的度数了,找出度数为1的即为叶子节点。

怎么用Tarjan求桥呢?根据Tarjan算法性质可知,若low[v]>dfn[u],则边(u,v)为桥(v被封死在子树内)

如图,若low[v]>dfn[u],则v被封死在u的子树内,删除点u,或者删除边(u,v),都将使v与u的祖先w不连通。

关于Tarjan求桥可见:http://hi.baidu.com/lydrainbowcat/item/f8a5ac223e092b52c28d591c

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<string>
  6 #include<queue>
  7 #include<algorithm>
  8 #include<map>
  9 #include<iomanip>
 10 #include<climits>
 11 #include<string.h>
 12 #include<cmath>
 13 #include<stdlib.h>
 14 #include<vector>
 15 #include<stack>
 16 #define INF 1000000007
 17 #define MAXN 40010
 18 #define Mod 1000007
 19 #define N 10010
 20 #define NN 30
 21 #define sigma_size 3
 22 const int maxn = 6e5 + 10;
 23 using namespace std;
 24 typedef long long LL;
 25
 26 struct Bridge {
 27     int u, v;
 28 }bg[2*N];
 29
 30 vector<int> G[N];
 31 int vis[N], low[N], dfn[N], Time;
 32 int fa[N], deg[N];
 33 int n, m, cnt;
 34 int u, v;
 35
 36 int findset(int x)
 37 {
 38     return fa[x] = fa[x] == x ? x : findset(fa[x]);
 39 }
 40
 41 void init()
 42 {
 43     for (int i = 0; i <= n; ++i) {
 44         G[i].clear();
 45         fa[i] = i;
 46     }
 47     memset(dfn,0,sizeof(dfn));
 48     memset(low,0,sizeof(low));
 49     memset(vis,0,sizeof(vis));
 50     memset(deg, 0, sizeof(deg));
 51     cnt = Time = 0;
 52     for (int i = 0; i < m;++ i) {
 53         cin >> u >> v;
 54         G[u].push_back(v);
 55         G[v].push_back(u);
 56     }
 57 }
 58
 59 void Tarjan(int u, int father)
 60 {
 61     low[u] = dfn[u] = ++Time;
 62     vis[u] = 1;
 63     for (int i = 0; i < G[u].size(); ++i) {
 64         int v = G[u][i];
 65         if (v == father) continue;
 66         if (!vis[v]){
 67             Tarjan(v, u);
 68             low[u] = min(low[u], low[v]);
 69             if (low[v] > dfn[u]){  //u->v为桥
 70                 bg[cnt].u = u;
 71                 bg[cnt].v = v;
 72                 cnt++;
 73             }
 74             else {                   //否则u,v同属一个连通分量,合并
 75                 int fx = findset(u);
 76                 int fy = findset(v);
 77                 if (fx != fy)
 78                     fa[fx] = fy;
 79             }
 80         }
 81         else {
 82             low[u] = min(low[u],dfn[v]);
 83         }
 84     }
 85 }
 86
 87 int main()
 88 {
 89     while (cin >> n >> m) {
 90         init();
 91         Tarjan(1,-1);
 92         for (int i = 0; i < cnt; ++i) {
 93             int fx = findset(bg[i].u);
 94             int fy = findset(bg[i].v);
 95             deg[fx]++;
 96             deg[fy]++;
 97         }
 98         int leaf = 0;
 99         for (int i = 1; i <= n; ++i)
100             if(deg[i] == 1) leaf++;
101         cout << (leaf + 1) / 2 << endl;
102     }
103     //system("pause");
104     return 0;
105 }
时间: 2024-12-15 18:21:51

uestc 方老师和农场的相关文章

UESTC 899 方老师和农场 --双连通分量的构造

首先将原图中的连通分量缩点,一定可以将原图缩成一棵树的形式,然后统计这棵树的叶子节点个数,答案就是(leaf+1)/2.这里不再证明,可以画个图看一下. (简单说明一下,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的.然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起.  --Byvoid) 怎么统计呢?用并查集缩点,可以知道,缩点后留下的边全部是原

UESTC_方老师和农场 2015 UESTC Training for Graph Theory&lt;Problem L&gt;

L - 方老师和农场 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 方老师重新开了一家农场,农场一共有N个节点和M条双向边,但是有一个很大的问题就是有比如一个农场A到达农场B只有一条路径,问至少添加多少条边使得任意两个农场之间的路径多于一条. Input 多组数据,EOF结束. 第1行:N和M. 第2到第M+1行:每一行2个数Ui和Vi,表示Ui到

uestc 方老师和缘分

关于怎么建图,自己还真是想不出来. 我觉得就是找到每个方老师所在的scc里的所有缘分吧.. 转自http://www.cnblogs.com/whatbeg/p/3765621.html 做法:建图,每个方老师和它想要的缘分之间连一条有向边,然后,在给出的初始匹配中反向建边,即如果第i个方老师现在找到的是缘分u,则建边u->i.这样求出所有的强连通分量,每个强连通分量中方老师和缘分的数目一定是相等的,所以每个方老师一定可以找到与他在同一个强连通分量里的缘分,因为强连通分量中每个点都是可达的,某个

uestc 方老师抢银行

参考的大牛的思路和代码 思路: 每次经过一个强连通分量时,都可以走完该分量从而使抢得的钱最多,可以把每个强连通分量缩点,用总的钱数代表该点的值.然后重新遍历新图,遇到网吧可以更新一下结果. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<string> 6 #include<queue> 7 #incl

uestc 方老师分身 I

题意有点晕啊.. 水题一道.. 分身去教室的时候是单向的,回来的时候也是单向的,这时候就要反过来跑一遍最短路了.. Dijkstra看着模板写的..sigh~ 要不是渣渣..还是记不住.. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<string> 6 #include<queue> 7 #incl

uestc 方老师炸弹

转自http://www.cnblogs.com/whatbeg/p/3765625.html Tarjan算法. 1.若u为根,且度大于1,则为割点 2.若u不为根,如果low[v]>=dfn[u],则u为割点(出现重边时可能导致等号,要判重边) 3.若low[v]>dfn[u],则边(u,v)为桥(封死在子树内),不操作. 求割点时,枚举所有与当前点u相连的点v: 1.是重边: 忽略 2.是树边: Tarjan(v),更新low[u]=min(low[u],low[v]); 子树个数cnt

uestc 方老师的分身IV

类似POJ1386,参考的大神的博客 首先明确思路: 是否为连通图(并查集解决) -> 是否存在欧拉路径  ->  是否存在欧拉回路   (是,则选取字典序最小的输出,否则直接输出可行解) 注意区分有向图和无线图存在欧拉路径或者欧拉回路的条件: 无向图: G为连通图,并且G仅有两个奇度节点或者无奇度节点 推论:1.当G是仅有两个奇度节点的连通图时,G的欧拉通路必以此两个结点为端点 2.当G时无奇度结点的连通图时,G必有欧拉回路 3.G为欧拉图(存在欧拉回路)的充分必要条件是G为无奇度节点的连通

uestc 方老师的分身 III 拓扑排序

没什么好说的.. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<string> 6 #include<queue> 7 #include<algorithm> 8 #include<map> 9 #include<iomanip> 10 #include<cl

uestc 方老师的分身 II

题意:求s到t走过边数大于k的最短路 思路:邻接表实现,用w[u][e]来维护(看的大牛博客),u表示当前点,e表示已经经过多少条边.感觉有点类似DP. 在边数大于k的处理上,发现还是使之等于k(K<=50),节省存储空间. spfa算法实现. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<string>