[HNOI2012]矿场搭建

题解:

首先显然这是要缩点的 缩点双

直接对割点之间的联通块判断一下连着几个割点

连0个 cnt*(cnt-1)/2

连1个 cnt

连2个 0

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 2000
bool iscut[N],ff[N];
ll head[N],dfn[N],low[N],cc,now,l,num,cnt,T,color[N];
ll n,m;
struct re{
  ll a,b,c;
}a[N];
void arr(ll x,ll y)
{
  a[++l].a=head[x];
  a[l].b=y;
  head[x]=l;
}
void tarjan(ll x,ll fa)
{
  dfn[x]=low[x]=++now;
  ll child=0;
  ll u=head[x];
  while (u)
  {
    ll v=a[u].b;
    if (!dfn[v])
    {
      child++;
      tarjan(v,x);
      low[x]=min(low[x],low[v]);
      if (low[v]>=dfn[x]) iscut[x]=1;
    } else
    {
      if (v!=fa) low[x]=min(low[x],dfn[v]);
    }
    u=a[u].a;
  }
  if (fa==0&&child==1) iscut[x]=0;
}
void dfs(ll x)
{
  color[x]=cc;
  if (iscut[x]) return;
  ff[x]=1; cnt++;
  ll u=head[x];
  while (u)
  {
    ll v=a[u].b;
    if (iscut[v]&&color[v]!=cc) num++;
    if (!ff[v]) dfs(v);
    u=a[u].a;
  }
}
int main()
{
  freopen("noi.in","r",stdin);
  freopen("noi.out","w",stdout);
  while (cin>>m&&m)
  {
    T++;
    n=0; memset(head,0,sizeof(head));
    now=l=0;
    memset(iscut,0,sizeof(iscut));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(ff,0,sizeof(ff));
    for (ll i=1;i<=m;i++)
    {
      ll x,y;
      cin>>x>>y;
      n=max(n,max(x,y));
      arr(x,y); arr(y,x);
    }
    for (ll i=1;i<=n;i++)
      if(!dfn[i]) tarjan(i,0);
    ll ans=1,ans1=0;
    for (ll i=1;i<=n;i++)
      if (!iscut[i]&&!ff[i])
      {
        num=0; cnt=0; cc++;
        dfs(i);
        if (num==0) ans*=max(1ll,cnt*(cnt-1)/2),ans1+=min(cnt,2ll);
        if (num==1) ans*=cnt,ans1++;
      }
    cout<<"Case "<<T<<": "<<ans1<<" "<<ans<<endl;
  }
  return 0;
} 
原文地址:https://www.cnblogs.com/yinwuxiao/p/8831728.html