SLOJ H7136. 「HNOI2012」矿场搭建
题目描述
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入格式
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N<=500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
输出格式
输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 264。输出格式参照以下输入输出样例。
输入输出样例
输入 #19 1 3 4 1 3 5 1 2 2 6 1 5 6 3 1 6 3 2 6 1 2 1 3 2 4 2 5 3 6 3 7 0输出 #1
Case 1: 2 4 Case 2: 4 1
说明/提示
Case 1 的四组解分别是 (2,4)(2,4),(3,4)(3,4),(4,5)(4,5),(4,6)(4,6);
Case 2 的一组解为 (4,5,6,7)(4,5,6,7)。
对于每组数据,设 mm 为各组 S, TS,T 中最大值,则有:
- 1≤m≤103;
- 各组 S,,T 构成的集合V=[1,m]∩Z。
- V中任意两点连通。
思路:肯定跟割点逃不出关系(蒟蒻勉强会一点tarjan,且容我恶补一波)还有组合数学与点双(再容我恶补一波)其实点双与割点本来就容易一起想到,每个点双之中都需要两个出口,方案数就用tarjan求完割点后用dfs遍历一遍连通块然后乘法原理就出来了
无非就是三种情况:
1.叶子节点(只含有一个割点的点双)必须建,叶子节点如果不建,割点塌了就没出口了
2.非叶节点(含有两个或两个以上的割点的点双)不用建,即使一个割点塌了也可以沿着另一个割点走到一个叶节点(即有出口的地方)
3.还有一种情况就是整个联通块都是点双(即不含割点的点双)这样我们讨论点双的大小
#include<bits/stdc++.h> using namespace std; struct edge{ int u,v,ne; }e[1010]; int n,m,sum,cnt,dfncnt,tmp,root,t,cases,h[1010],dfn[1010],low[1010],vis[1010]; bool cut[1010]; void add(int u,int v){ sum++; e[sum].u = u; e[sum].v = v; e[sum].ne = h[u]; h[u] = sum; } void tarjan(int u,int fa){ dfn[u] = low[u] = ++dfncnt; for(int i = h[u];i;i = e[i].ne){ int v = e[i].v; if(!dfn[v]){ tarjan(v,u); low[u] = min(low[u],low[v]); if(low[v]>=dfn[u]){ if(u==root) tmp++; else cut[u] = true; } }else if(v!=fa) low[u] = min(low[u],dfn[v]); } } void dfs(int x){ vis[x] = t; if(cut[x]) return; cnt++; for(int i = h[x];i;i = e[i].ne){ int v = e[i].v; if(cut[v]&&vis[v]!=t){ sum++; vis[v] = t; } if(!vis[v]) dfs(v); } } int main(){ scanf("%d",&m); while(m){ long long ans1 = 0,ans2 = 1; memset(h,0,sizeof(h)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(cut,0,sizeof(cut)); memset(vis,0,sizeof(vis)); cnt = dfncnt = n = t = 0; for(int i = 1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); n = max(n,max(x,y)); add(x,y);add(y,x); } for(int i = 1;i<=n;i++){ if(!dfn[i]) tarjan(root = i,0); if(tmp>=2) cut[root] = 1; tmp = 0; } for(int i = 1;i<=n;i++) if(!vis[i]&&!cut[i]){ t++; cnt = sum = 0; dfs(i); if(!sum) ans1+=2,ans2*=cnt*(cnt-1)/2; if(sum==1) ans1++,ans2*=cnt; } printf("Case %d: %lld %lld\n",++cases,ans1,ans2); scanf("%d",&m); } return 0; }
综上所述,我是蒟蒻
2022-10-27 14:47:21
标签:cut,洛谷,vis,int,sum,割点,HNOI2012,low,P3225 From: https://www.cnblogs.com/cztq/p/16832207.html