P3225 [HNOI2012]矿场搭建

斯德哥尔摩

2018-08-01 21:36:46

Personal

[P3225 [HNOI2012]矿场搭建](https://www.luogu.org/problemnew/show/P3225) 思路:找出割点和所有的双连通分量,然后统计每个双连通分量里的个点的个数分情况讨论。 1. 若该连通分量里有不少于两个割点,则它是安全的,因为无论哪个割点炸了,里面的点可以通过其他的没炸的割点跑到其他的双连通分量里去。 2. 若该连通分量里只有一个割点,那么如果这个割点炸了,则里面的点就不可能跑到其他的双连通分量里去了,所以要在这个割点里建一个出口。 3. 若该连通分量里一个割点也没有,说明它与外界完全不连通,这时如果只建一个出口的话,那么如果这个出口炸了就$GG$,所以还需要另一个出口“以防万一”(即建两个出口) 对于方案数的话,我们发现如果要建出口的话,该双连通分量里的任何一个非割点的节点都是可以的,因此用一下组合数学就可以搞定了。 附代码: ```cpp #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define MAXN 510 using namespace std; int n,m,c,d,num,times,size; long long ans,sum; int head[MAXN],deep[MAXN],low[MAXN],fa[MAXN],vis[MAXN]; bool flag[MAXN]; struct Graph{ int next,to; }a[MAXN<<1]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline void add(int x,int y){ a[c].to=y;a[c].next=head[x];head[x]=c++; a[c].to=x;a[c].next=head[y];head[y]=c++; } void dfs(int x){ int s=0; deep[x]=low[x]=d++; for(int i=head[x];i;i=a[i].next){ int v=a[i].to; if(!deep[v]){ fa[v]=fa[x]; dfs(v); low[x]=min(low[x],low[v]); if(low[v]>=deep[x]&&x!=fa[x])flag[x]=true; if(x==fa[x])s++; } low[x]=min(low[x],deep[v]); } if(x==fa[x]&&s>=2)flag[fa[x]]=true; } void answer(int x){ size++; vis[x]=times; for(int i=head[x];i;i=a[i].next){ int v=a[i].to; if(vis[v]!=times&&flag[v]){ num++; vis[v]=times; } if(!vis[v]&&!flag[v])answer(v); } } void work(){ for(int i=1;i<=n;i++) if(!vis[i]&&!flag[i]){ size=num=0; times++; answer(i); if(num==0){ans+=2;sum*=size*(size-1)/2;} if(num==1){ans++;sum*=size;} } printf("%lld %lld\n",ans,sum); } void init(){ c=d=sum=1; n=times=ans=0; memset(head,0,sizeof(head)); memset(deep,0,sizeof(deep)); memset(low,0,sizeof(low)); memset(flag,false,sizeof(flag)); memset(vis,0,sizeof(vis)); int x,y; for(int i=1;i<=m;i++){ x=read();y=read(); n=max(n,max(x,y)); add(x,y); } for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=n;i++)if(!deep[i])dfs(i); } int main(){ for(int cases=1;;cases++){ m=read(); if(m==0)break; printf("Case %d: ",cases); init(); work(); } return 0; } ```