zoukankan      html  css  js  c++  java
  • 【BZOJ2730】[HNOI2012] 矿场搭建(找割点)

    点此看题面

    大致题意: 一张无向图,要求你在去掉任意一个节点之后,剩余的每个节点都能到达一个救援出口,问至少需要几个救援出口。

    第一步:(Tarjan)求割点

    首先,我们要跑一遍(Tarjan)来求出割点

    求完割点后,这样我们就能求出原图除了割点以外其他点所形成的若干个联通块(相当于把割点去掉之后形成的联通块)。

    第二步:分类讨论

    接下来,我们要对上面求出来的每一个联通块相邻的割点个数进行分类讨论:

    • 如果当前联通块相邻的割点个数为0

      显然对于这个联通块我们需要造2个救援出口,不然万一一个救援出口崩塌了,就出不去了。

      而方案数就相当于(C_{Size}^2),即(frac{Size(Size-1)}2)

    • 如果当前联通块相邻的割点个数为1

      那么对于这个联通块我们只需要造一个救援出口,因为就算救援出口崩塌了,还可以通过割点去另一个联通块内的救援出口。

      而方案数就是(Size)

    • 如果当前联通块相邻的割点个数大于等于2

      那么对于这个联通块我们就不需要造救援出口了,因为就算某个割点崩塌了,我们依然可以通过另一个割点到达其它联通块的救援出口。

    这样代码实现就不难了。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define abs(x) ((x)<0?-(x):(x))
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define tc() (A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++)
    #define pc(ch) (pp_<100000?pp[pp_++]=ch:(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=ch))
    #define N 500
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    int pp_=0;char ff[100000],*A=ff,*B=ff,pp[100000];
    using namespace std;
    int n,m,ans1=0,ee=0,cnt=0,d=0,top=0,Exist[N+5],lnk[N+5],dfn[N+5],low[N+5],vis[N+5],IsCut[N+5],used[N+5],Size[N+5],tot[N+5],Stack[N+5];
    ull ans2=1;
    struct edge
    {
    	int to,nxt;
    }e[2*N+5];
    inline void read(int &x)
    {
    	x=0;static char ch;
    	while(!isdigit(ch=tc()));
    	while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
    }
    inline void write(ull x)
    {
    	if(x>9) write(x/10);
    	pc(x%10+'0');
    }
    inline void Tarjan(int x,int lst)//用Tarjan找割点
    {
    	register int i,tot=0;
    	for(dfn[x]=low[x]=++d,i=lnk[x];i;i=e[i].nxt)
    	{
    		if(!(e[i].to^lst)) continue;
    		if(!dfn[e[i].to]) 
    		{
    			Tarjan(e[i].to,x),low[x]=min(low[x],low[e[i].to]),++tot;
    			if(lst&&low[e[i].to]>=dfn[x]) IsCut[x]=1;
    		}
    		else low[x]=min(low[x],dfn[e[i].to]);
    	}
    	if(!lst&&tot>=2) IsCut[x]=1;
    }
    inline void dfs(int x)//dfs遍历除去割点后的一个联通块
    {
    	register int i,v;
    	for(Size[x]=vis[x]=1,tot[x]=0,i=lnk[x];i;i=e[i].nxt)//枚举每一个相邻的节点
    	{
    		if(IsCut[v=e[i].to])//如果这个节点是割点 
    		{
    			if(!used[v]) used[Stack[++top]=v]=1,++tot[x];//如果这个割点没有访问过,就标记这个割点已访问,并将这个联通块相邻的割点个数加1
    			continue;//跳过
    		}
    		if(!vis[v]) dfs(v),Size[x]+=Size[v],tot[x]+=tot[v];//如果这个节点没被访问过,就去访问这个节点,并更新当前节点信息
    	}
    }
    int main()
    {
    	register int i,j,x,y,T=0;
    	while(read(n),n)
    	{
    		for(ans1=ee=0,ans2=i=1;i<=n+1;++i) lnk[i]=dfn[i]=low[i]=IsCut[i]=Exist[i]=vis[i]=0;//初始化,将数组清空
    		for(i=1;i<=n;++i) read(x),read(y),add(x,y),add(y,x),Exist[x]=Exist[y]=1;
    		for(i=1;i<=n+1;++i) if(Exist[i]&&!dfn[i]) Tarjan(i,0);//Tarjan求割点
    		for(i=1;i<=n+1;++i) 
    		{
    			if(!Exist[i]||IsCut[i]||vis[i]) continue;//如果这个节点不存在,或这个节点是割点,或这个节点已经被访问过,就跳过
    			top=0,dfs(i);//dfs遍历这个联通块
    			while(top) used[Stack[top--]]=0;//将访问过的割点标记为未访问
    			if(!tot[i]) ans1+=2,ans2*=1LL*Size[i]*(Size[i]-1)>>1;//如果这个联通块相邻的割点数为0,就需要加两个救援出口,方案数为Size(Size-1)/2
    			else if(tot[i]==1) ++ans1,ans2*=Size[i]; //如果这个联通块相邻的割点数为1,就需要加一个救援出口,方案数为Size
    			//如果这个联通块相邻的割点数大于等于2,就不需要加救援出口了
    		}
    		pc('C'),pc('a'),pc('s'),pc('e'),pc(' '),write(++T),pc(':'),pc(' '),write(ans1),pc(' '),write(ans2),pc('
    ');//输出答案
    	}
    	return fwrite(pp,1,pp_,stdout),0;
    }
    
  • 相关阅读:
    C阶段【01】
    Xcode常用快捷键的使用
    eclipse中添加web app libraries
    hibernate 连接SQL SERVER2008
    hibernate配置入门(个人总结)
    项目编译PNG报错
    项目archive打包编译报错
    项目上传
    Git本地项目上传,版本管理工具与GitHub的简单结合使用
    将制定目录下的内容复制到另一个路径下
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2730.html
Copyright © 2011-2022 走看看