博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
洛谷 - P3225 - 矿场搭建 - 双连通分量
阅读量:4680 次
发布时间:2019-06-09

本文共 3226 字,大约阅读时间需要 10 分钟。

这个东西有点绕。

最平凡的情况,整个原图只有一个点,那么它坍塌了之后就没有点了,不需要进行任何逃生。否则,当一个点坍塌之后,每个其他点的工人都要逃向逃生出口。

首先把双连通分量缩点,缩点完成之后必定是一片森林,树与树之间是乘法原理。

单独考虑一棵树。

平凡的情况,只有一个根节点,没有边。

这种情况下,假如原图中该双连通分量中有>=2个点,则这个双连通分量中要建立2个出口(其中一个出口坍塌了,都可以从另一个出口走),否则假如只有一个点,则只需要建立1个出口。

不平凡的情况,是一棵无根树。必定存在叶子,每个叶子都必须建立1个出口。这个出口必须建立在非原图割点的位置,假如原图这个双连通分量没有割点(这个双连通分量只有一个点,去掉都没没事),则不需要加。

不是叶子的情况,当其他节点坍塌,则必定可以去到至少1个叶子节点逃生,不需要建立逃生出口。当自己(且自己只有一个点)坍塌,每个子树自己跑去自己的叶子就可以了。

那么先特判掉n=1的情况。然后在原图中跑出原图的割点。然后遍历一遍原图的点,从没有被访问过的非割点进入,搜索连接的所有非割点,这些非割点都属于这个双连通分量,而搜索到的割点也属于这个双连通分量不过不需要进去。这样会漏掉两个割点之间的所有非割点和割点,不过这貌似没有问题?因为假如都有两个非割点了,答案就是0了。而且环的另一个半(或者1/3,或者1/4)也会搜索到那两个割点的,反正都是0。

假如只有一个割点,则这个是缩点后图的叶子,贡献C(非割点数量,1)。

假如有多于两个割点,则不贡献,少数的非割点和割点都无所谓了

假如没有割点,则这个双连通分量缩点之后只剩下自己一个点作为树,那么也是贡献C(非割点数量,2),(当该双连通分量有多于两个点时),或者0(当该双连通分量只有1个点时)。

各个双连通分量之间选点独立,各个树之间选点独立,全部乘法叠加。

从这道题可以得到一个常识:每个割点不一定仅属于一个双连通分量,比如一个dio型图。

#include
using namespace std;typedef long long ll;const int MAXN = 505;int n;vector
G[MAXN];int dfn[MAXN], low[MAXN], dfncnt;bool cut[MAXN];int vis[MAXN], viscol;int cntcut, cntncut;void init() { for(int i = 1; i <= 500; ++i) G[i].clear(); dfncnt = 0; memset(cut, false, sizeof(cut)); memset(vis, false, sizeof(vis)); memset(dfn, false, sizeof(dfn)); memset(low, false, sizeof(low)); viscol = 1;}void tarjan(int u, int p) { low[u] = dfn[u] = ++dfncnt; ll sum = 0; int ch = 0; for(auto v : G[u]) { if(!dfn[v]) { tarjan(v, u); low[u] = min(low[u], low[v]); if(p == -1) ch++; else if(low[v] >= dfn[u]) cut[u] = true; } else low[u] = min(low[u], dfn[v]); } if(ch >= 2) cut[u] = true;}void dfs(int u, int p) { //cout << "u=" << u << endl; vis[u] = viscol; ++cntncut; for(auto v : G[u]) { if(vis[v] != viscol) { vis[v] = viscol; if(cut[v]) ++cntcut; else dfs(v, u); } }}int main() {#ifdef Yinku freopen("Yinku.in", "r", stdin);#endif // Yinku int m, kase = 1; while(~scanf("%d", &m)) { if(m == 0) return 0; init(); n = 0; for(int i = 1, u, v; i <= m; ++i) { scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); n = max(n, max(u, v)); } for(int i = 1; i <= n; ++i) { if(!dfn[i]) tarjan(i, -1); /*if(cut[i]) printf("cut=%d\n",i);*/ } unsigned long long sum1 = 0; unsigned long long sum2 = 1; for(int i = 1; i <= n; ++i) { if(!vis[i] && !cut[i]) { //cout<<"viscol="<
<
= 2) { sum1 += 2; sum2 *= cntncut * (cntncut - 1) / 2; } else { sum1 += 1; } } else if(cntcut == 1) { ++sum1; sum2 *= cntncut; } else ; viscol++; } } printf("Case %d: %llu %llu\n", kase++, sum1, sum2); } return 0;}

转载于:https://www.cnblogs.com/Yinku/p/11363294.html

你可能感兴趣的文章
第三组的抓包作业
查看>>
ILNumerics项目的应用之线性方程
查看>>
django考点
查看>>
python-socket
查看>>
.NET 分布式技术比较
查看>>
SpringMVC视频
查看>>
Android中intent如何传递自定义数据类型
查看>>
Android蓝牙音乐获取歌曲信息
查看>>
android基础---->子线程更新UI
查看>>
SharedPreferences
查看>>
转载 线程池之ThreadPool类与辅助线程 - <第二篇>
查看>>
解决windows 10 9926 中vmware安装的虚拟机无法桥接上网的问题
查看>>
js获取元素样式
查看>>
合并排序(C语言实现)
查看>>
sql 计算两时间或日期 的相差的 年、 月、 日、时、分、秒,年、月、日分别的提取...
查看>>
HDU 1176免费馅饼 DP数塔问题转化
查看>>
十进制二进制转换
查看>>
shiro实战系列(七)之Realm
查看>>
数据库持久化比较
查看>>
超像素、语义分割、实例分割、全景分割 傻傻分不清?
查看>>