• HNOI2012 矿场搭建 v-DCC缩点+分类讨论


    HNOI2012 矿场搭建

    题目传送

    sol:

    首先需要对v-DCC缩点,对于缩点后的每一个连通块需分类讨论一下。

    对于每一个连通块(v-DCC):

    1、不存在割点。需要建两个出口(毁掉一个还得有一个)。

    2、存在一个割点。需要一个出口。(毁掉割点,走连通块内出口;毁掉连通块内出口,走割点到别的连通块出口)。

    3、存在至少两个割点。不需要出口。(不管毁掉哪里,都能到别的连通块出口)。

    方案的话就是把各个连通块的方案乘起来就好了。

    #include<bits/stdc++.h>
    #define IL inline
    #define RG register
    #define DB double
    #define LL long long
    using namespace std;
    
    const int N=505;
    
    LL ans2;
    int n,m,RT,cnt,tot,top,ans1,Time,c[N],sta[N],low[N],dfn[N],cut[N],sum[N],head[N],eDcc[N][N];
    
    struct EDGE{int next,to;}e[N<<1];
    
    IL int gi() {
    	RG int x=0,p=1; RG char ch=getchar();
    	while(ch<'0'||ch>'9') {if(ch=='-') p=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
    	return x*p;
    }
    
    IL void New_case() {
    	n=tot=cnt=top=Time=0;
    	memset(c,0,sizeof(c));
    	memset(&e,0,sizeof(e));
    	memset(head,0,sizeof(head));
    	memset(dfn,0,sizeof(dfn));
    	memset(low,0,sizeof(low));
    	memset(cut,0,sizeof(cut));
    	memset(sum,0,sizeof(sum));
    }
     
    IL void make(int a,int b) {
    	e[++tot]=(EDGE){head[a],b},head[a]=tot;
    	e[++tot]=(EDGE){head[b],a},head[b]=tot;
    }
    
    void Tarjan(int x) {
    	RG int i,k,y,num=0;
    	dfn[x]=low[x]=++Time,sta[++top]=x;
    	for(i=head[x];i;i=e[i].next)
    		if(!dfn[y=e[i].to]) {
    			Tarjan(y),++num,low[x]=min(low[x],low[y]);
    			if(low[y]>=dfn[x]) {
    				if(x!=RT||num>1) cut[x]=1;
    				++cnt;
    				do{
    					k=sta[top--];
    					eDcc[cnt][++c[cnt]]=k;
    				}while(k!=y);
    				eDcc[cnt][++c[cnt]]=x;
    			}
    		}
    		else low[x]=min(low[x],dfn[y]);
    }
    
    int main()
    {
    	RG int i,j,x,y,Case=0;
    	while(scanf("%d",&m)!=EOF&&m) {
    		New_case();
    		for(i=1;i<=m;++i)
    			x=gi(),y=gi(),n=max(n,max(x,y)),make(x,y);
    		for(i=1;i<=n;++i)
    			if(!dfn[i]) RT=i,Tarjan(i);
    		for(i=1;i<=cnt;++i)
    			for(j=1;j<=c[i];++j) sum[i]+=cut[eDcc[i][j]];
    		for(i=1,ans1=0,ans2=1;i<=cnt;++i)
    			if(!sum[i]) ans1+=2,ans2*=c[i]*(c[i]-1)/2;
    			else if(sum[i]==1) ++ans1,ans2*=c[i]-1;
    		printf("Case %d: %d %lld
    ",++Case,ans1,ans2);
    	}
    	return 0;
    }
    
  • 相关阅读:
    二维数组求和
    mysql 常用函数
    3月17日 45道T-SQL查找 习题
    查询语句
    T-SQL 增删改查操作
    <转jerrylsxu> HTML语法大全
    1月25日 作业 多线程
    1月22日- 链表和哈希算法
    1月22日作业
    1月21日
  • 原文地址:https://www.cnblogs.com/Bhllx/p/11258385.html
Copyright © 2020-2023  润新知