BZOJ-2730矿场搭建 Tarjan 双连通分量
Posted DaD3zZ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ-2730矿场搭建 Tarjan 双连通分量相关的知识,希望对你有一定的参考价值。
2730: [HNOI2012]矿场搭建
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1602 Solved: 751
[Submit][Status][Discuss]
Description
Input
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
Output
输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。
Sample Input
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
Sample Output
Case 2: 4 1
HINT
Source
Solution
对于删除一个点,其余点要有出路,显然是和割点有关,那么我们求出所有割点,以及双连通分量。
对于一个双联通分量中,如果我们只删除一个割点或非割点,那么如果还有其余割点能使其余点到关键点,那么显然是不需要额外考虑的;如果没有其余的割点令我们到关键点,那么我们需要新建额外的关键点
分情况讨论,如果一个双联通分量里,有两个及以上割点,那么这个双联通分量里面是不需要额外建的
如果一个双联通分量里只有一个或没有割点,那么我们只需要再建一个即可。
至于方案数,可以利用乘法原理,我们把每个连通分量里的每个可以用来建成关键点的点用乘法统计起来即可
Code
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<vector> using namespace std; #define LL long long inline int read() { int x=0,f=1; char ch=getchar(); while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();} while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();} return x*f; } #define MAXM 1010 #define MAXN 80010 int N,M,tot; LL sum; struct EdgeNode{int next,to,from;}edge[MAXM<<1]; int head[MAXN],cnt=1; void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;} void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);} #define Pa pair<int,int> vector<int>BCC[MAXN]; Pa st[MAXM]; int top; int dfn[MAXN],low[MAXN],dfsn,cut[MAXN],bcc,belong[MAXN]; void Tarjan(int now,int last) { dfn[now]=low[now]=++dfsn; int son=0; for (int i=head[now]; i; i=edge[i].next) if (!dfn[edge[i].to]) { st[++top]=make_pair(now,edge[i].to); son++; Tarjan(edge[i].to,now); low[now]=min(low[now],low[edge[i].to]); if (dfn[now]<=low[edge[i].to]) { cut[now]=1; bcc++; BCC[bcc].clear(); int tnow=-1,tto=-1; while (1) { tnow=st[top].first,tto=st[top].second; top--; if (belong[tnow]!=bcc) BCC[bcc].push_back(tnow),belong[tnow]=bcc; if (belong[tto]!=bcc) BCC[bcc].push_back(tto),belong[tto]=bcc; if (tnow==now && tto==edge[i].to) break; } } } else if (dfn[edge[i].to]<dfn[now] && edge[i].to!=last) st[++top]=make_pair(now,edge[i].to),low[now]=min(low[now],dfn[edge[i].to]); if (last<0 && son==1) cut[now]=0; } int main() { int cas=0; while (scanf("%d",&M)) { if (M==0) break; N=0; cnt=1; memset(head,0,sizeof(head)); top=0; bcc=0; memset(st,0,sizeof(st)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); bcc=0,dfsn=0; memset(cut,0,sizeof(cut)); memset(belong,0,sizeof(belong)); for (int x,y,i=1; i<=M; i++) x=read(),y=read(),InsertEdge(x,y),N=max(N,max(x,y)); for (int i=1; i<=N; i++) if (!dfn[i]) Tarjan(i,-1); // for (int i=1; i<=N; i++) printf("%d %d %d\n",dfn[i],belong[i],cut[i]); tot=0,sum=1; // printf("%d\n",bcc); // for (int i=1; i<=bcc; i++) printf("%d ",BCC[i].size()); puts(""); for (int i=1,num=0,sz=BCC[i].size()-1; i<=bcc; i++,num=0,sz=BCC[i].size()-1) { // printf("%d %d %I64d\n",num,tot,sum); for (int j=0; j<=sz; j++) if (cut[BCC[i][j]]) num++; if (num==1) tot++,sum*=(LL)(BCC[i].size()-num); if (bcc==1) {tot=2; sum=(LL)BCC[1].size()*(BCC[1].size()-1)/2; break;} } printf("Case %d: %d %lld\n",++cas,tot,sum); } return 0; }
以上是关于BZOJ-2730矿场搭建 Tarjan 双连通分量的主要内容,如果未能解决你的问题,请参考以下文章