[省选前题目整理][BZOJ 2730][HNOI 2012]矿场搭建(Tarjan求割点+计数问题)
2015-03-30 09:04
417 查看
题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=2730思路
注意到点双联通分量的性质:去掉点双中任意一个点,点双中其他点仍然双联通。因此可以发现,在整个图中,一个只含有一个割点的点双联通分量中必须建一个井口,因为若这个点双的割点塌陷,那么这个点双中所有点都将与整个图其他的联通部分不再联通,因此必须建一个井口,而且这个井口绝对不能建立在割点上(建立在割点上,那么割点塌了,这个点双中的矿工照样死翘翘大笑)。但是一个含有多个割点的点双不需要建立井口,因为如果这个点双中塌了一个割点,但是其他割点仍然安然无恙,这个点双中的矿工可以从这些完好的割点撤离。因此我们只需要先通过Tarjan找出整个图的所有割点和点双,并给每个割点做标记。对于每个只有一个割点的点双,修一个矿井,这样第一问就解决了。
那么还有第二问,其实也比较好做,观察发现每个需要放井口的点双(只有一个割点的点双)中除了割点以外,其他位置都可以修井口,因此用乘法计数便可求出总方案数,第二问也解决了。
注意还有一个特判:如果整个图都是点双,那么只需要修建两个井口(如果只修建一个井口,有可能最终这个井口塌了,剩余的图没井口了,矿工闷死在井里出不来大笑),最终的方案数为在n个不同的点中选2个点的无序组合的方案数,也就是n*(n-1)/2。
代码
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include <vector> #define MAXE 2000 #define MAXV 2000 using namespace std; typedef long long int LL; int n,m; LL ans1=0,ans2=1; struct edge { int u,v,next; }edges[MAXE]; int head[MAXV],nCount=0; void AddEdge(int U,int V) { edges[++nCount].u=U; edges[nCount].v=V; edges[nCount].next=head[U]; head[U]=nCount; } int low[MAXV],dfn[MAXV],dfs_cnt=0; int belong[MAXV],bcc_cnt; //belong[i]=点i所属的点双编号 int stack[2*MAXE],top=0; //DFS用的栈 bool isCut[MAXV]; vector<int>bcc[MAXV]; //保存每个点双中的点 void Tarjan_BCC(int u,int fa) { low[u]=dfn[u]=++dfs_cnt; int child_cnt=0; //DFS树中的儿子个数 for(int p=head[u];p!=-1;p=edges[p].next) { int v=edges[p].v; if(!dfn[v]) //u->v为DFS树边 { stack[++top]=p; child_cnt++; Tarjan_BCC(v,u); low[u]=min(low[u],low[v]); if(dfn[u]<=low[v]) //u为割点 { isCut[u]=true; bcc_cnt++; bcc[bcc_cnt].clear(); while(1) { int p1=stack[top--]; if(belong[edges[p1].u]!=bcc_cnt) { bcc[bcc_cnt].push_back(edges[p1].u); belong[edges[p1].u]=bcc_cnt; } if(belong[edges[p1].v]!=bcc_cnt) { bcc[bcc_cnt].push_back(edges[p1].v); belong[edges[p1].v]=bcc_cnt; } if(edges[p1].u==u&&edges[p1].v==v) break; } } } else if(dfn[v]<dfn[u]&&v!=fa) //u->v是回边 { stack[++top]=p; low[u]=min(low[u],dfn[v]); } } if(fa<0&&child_cnt==1) //特判 isCut[u]=false; } int main() { int TestCase=0; while(scanf("%d",&m)!=EOF&&m) { ans1=0,ans2=1; nCount=n=bcc_cnt=dfs_cnt=top=0; memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(belong,0,sizeof(belong)); memset(isCut,0,sizeof(isCut)); int u,v; for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); n=max(n,u); n=max(n,v); AddEdge(u,v); AddEdge(v,u); //!!!! } for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan_BCC(i,-1); for(int i=1;i<=bcc_cnt;i++) //遍历第i个点双 { int cut_cnt=0; //该点双中的割点个数 for(int j=0;j<(int)bcc[i].size();j++) if(isCut[bcc[i][j]]) cut_cnt++; if(cut_cnt==1) //该点双中只有一个割点,则必须在该矿中建立一个井口 { ans1++; ans2*=(LL)(bcc[i].size()-cut_cnt); } } if(bcc_cnt==1) //只有一个点双的情况 { ans1=2; ans2=bcc[1].size()*(bcc[1].size()-1)/2; } printf("Case %d: %lld %lld\n",++TestCase,ans1,ans2); } return 0; }
相关文章推荐
- [BZOJ 2730][HNOI 2012]矿场搭建(Tarjan求割点与桥+计数问题)
- BZOJ 2730: [HNOI2012]矿场搭建 tarjan割点
- [BZOJ]2730: [HNOI2012]矿场搭建 Tarjan求割点
- BZOJ 2730: [HNOI2012]矿场搭建 | tarjan
- bzoj 2730: [HNOI2012]矿场搭建——tarjan求点双
- bzoj 2730 HNOI2012 矿场搭建 tarjan
- bzoj 2730: [HNOI2012]矿场搭建(tarjan求点双连通分量)
- 【BZOJ2730】[HNOI2012]矿场搭建 Tarjan
- bzoj 2730 HNOI2012 矿场搭建 tarjan
- 【tarjan求割顶】BZOJ2730-[HNOI2012]矿场搭建
- BZOJ2730: [HNOI2012]矿场搭建 Tarjan求割点
- BZOJ 2730: [HNOI2012]矿场搭建( tarjan )
- bzoj 2730: [HNOI2012]矿场搭建【tarjan】
- BZOJ 2730 HNOI2012 矿场搭建 Tarjan
- bzoj2730: [HNOI2012]矿场搭建
- 【BZOJ 2730】 [HNOI2012]矿场搭建
- 【bzoj2730】 HNOI2012—矿场搭建
- bzoj2730 [HNOI2012]矿场搭建 (UVAlive5135 Mining Your Own Business)
- [BZOJ2730][HNOI2012]矿场搭建
- BZOJ 2730: [HNOI2012]矿场搭建