bzoj2730 [HNOI2012]矿场搭建 tarjan 点双连通分量

来源:互联网 发布:java命令行导入jar包 编辑:程序博客网 时间:2024/04/28 21:14

题意:一个无向非连通图,要求每个点的矿工都能走到救援口,可能会把任意一个点ban掉,要求最少的救援口,使得每一个点都能走到救援口,以及最少救援口的方案数。

首先很明显,如果ban掉的点不是割点没影响。
所以一个很明显的结论就是我们每一个块内设一个救援口,那么第一个问题的答案就是块的个数。
还有特例就是,如果一个块链接两个割点,那我ban掉一个以后还有另外一个,所以这种情况下,这个块内不用设立新的点。
以及,如果一个块内没有割点,那么需要设立两个。
方案数的话,没有割点的块是sz*(sz-1)
一个割点是sz咯,乘法原理很简单的吧。
注意是无向图所以是点双。感觉很久没打都快忘了。

#include<cstdio>#include<algorithm>#include<cstring>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;const int N=1e3+5;typedef long long ll;int head[N],next[N],go[N];int dfn[N],low[N],ans1,tot,vis[N];int sz,cnt,deg,root,cut[N],num;ll ans2;int n,m;inline void add(int x,int y){    go[++tot]=y;    next[tot]=head[x];    head[x]=tot;}inline void dfs(int x){    vis[x]=cnt;    if (cut[x])return;    sz++;    for(int i=head[x];i;i=next[i])    {        int v=go[i];        if (cut[v]&&vis[v]!=cnt)num++,vis[v]=cnt;        if (!vis[v])dfs(v);    }} inline void tarjan(int x,int fa){    dfn[x]=low[x]=++sz;    for(int i=head[x];i;i=next[i])    {        int v=go[i];        if (!dfn[v])        {            tarjan(v,x);            low[x]=min(low[x],low[v]);            if (low[v]>=dfn[x])            {                if (x==root)deg++;                else cut[x]=1;            }        }        else if (v!=fa)low[x]=min(low[x],dfn[v]);    }}int main(){    int cas=0;    while (1)    {        scanf("%d",&m);        if (!m)break;        cas++;        memset(vis,0,sizeof(vis));        memset(head,0,sizeof(head));        memset(dfn,0,sizeof(dfn));        memset(cut,0,sizeof(cut));        tot=sz=cnt=ans1=n=0;        ans2=1;        fo(i,1,m)        {            int x,y;            scanf("%d%d",&x,&y);            n=max(n,x);            n=max(n,y);            add(x,y);            add(y,x);        }        fo(i,1,n)        {            if (!dfn[i])tarjan(root=i,i);            if (deg>=2)cut[i]=1;            deg=0;        }        fo(i,1,n)        if (!vis[i]&&!cut[i])        {            cnt++;sz=num=0;            dfs(i);            if (!num)ans1+=2,ans2*=sz*(sz-1)/2;            else if (num==1)ans1++,ans2*=sz;        }        printf("Case %d: %d %lld\n",cas,ans1,ans2);    }}
原创粉丝点击