HDU3844 Mining Your Own Business

问题描述
John Digger是一个大型illudium phosdex矿的所有者。该矿山由一系列隧道组成,这些隧道在各个大型交叉口相遇。与一些业主不同,Digger实际上关心他的工人的福利,并担心矿山的布局。具体来说,他担心可能会出现一个交汇处,如果发生倒塌,会将矿井的一个区域的工人与其他工人隔离开来(如你所知,illudium phosdex非常不稳定)。为了解决这个问题,他希望从交叉点到地面安装特殊的逃生轴。他可以在每个交叉点安装一个逃生轴,但Digger并不关心他的工人那么多。相反,他想安装最少数量的逃生轴,这样如果任何一个交叉点坍塌,所有在交叉点坍塌中幸存的工人将拥有通往地面的路径。
编写一个程序来计算最小的逃生轴数量以及可以安装这个最小数量的逃生轴的总方式。
输入
输入包含几个测试用例。每种情况的第一行包含正整数N(N <= 5×10 ^ 4),表示矿井隧道的数量。在此之后是N行,每行包含两个不同的整数s和t,其中s和t是结号。连接点从1开始连续编号。每对连接点最多由一个隧道连接。每组矿井隧道形成一个连接单元(也就是说,您可以从任何一个交叉点到达任何其他交叉点)。
最后一个测试用例后面是一个包含单个零的行。
输出
对于每个测试案例,显示其案例编号,然后显示矿井隧道系统所需的最小数量的逃生轴以及这些逃生轴的安装方案数。您可以假设结果适合带符号的64位整数。

题意:给定一个无向连通图,选择一些特殊点,当图中任意一个点(也有可能是特殊点)被删去的时候,其他点至少能到达一个特殊点。

显然我们不会将割点作为特殊点,因为如果割点被去掉,他所在的dcc仍然要有特殊点,而此时割点是没有必要做特殊点的,对于包含两个及以上割点的dcc也是不需要特殊点的,即使一个割点被去掉,这个dcc仍然可以通过其他割点到其他dcc,因此特殊点一定在割点数量为1的dcc内,方案数就很显然了,乘起来就可以了。另外如果整张图是一个dcc,之前的分析就没有用了,此时显然需要2个特殊点,方案数为C(n,2)。另外这个题没有给点的个数,但保证是连续的(不然又要离散化了……),所以可以在输入的时候取max来求得。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define int long long
#define MAXN 100010
#define LL long long
#define ma(x) memset(x,0,sizeof(x))
using namespace std;
struct edge
{
int u,v,nxt;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].nxt
#define u2(x) ed2[x].u
#define v2(x) ed2[x].v
#define n2(x) ed2[x].nxt
}ed[MAXN*10],ed2[MAXN*10];
int first[MAXN],num_e;
#define f(x) first[x]
int first2[MAXN],num_e2;
#define f2(x) first2[x] int n,m;
int dfn[MAXN],low[MAXN],num,root;
int stack[MAXN],top,cnt;
bool cut[MAXN];
vector<int> dcc[MAXN];
int c[MAXN],du[MAXN];
void tarjan(int x)
{
dfn[x]=low[x]=++num;
stack[++top]=x;
if(x==root&&!f(x)){dcc[++cnt].push_back(x);return;}
int flag=0;
for(int i=f(x);i;i=n(i))
if(!dfn[v(i)])
{
tarjan(v(i)),low[x]=min(low[v(i)],low[x]);
if(low[v(i)]>=dfn[x])
{
flag++;
if(x!=root||flag>1)cut[x]=1;
cnt++;int z;
do{dcc[cnt].push_back(z=stack[top--]);}while(z!=v(i));
dcc[cnt].push_back(x);
}
}
else low[x]=min(low[x],dfn[v(i)]);
}
void init()
{
ma(first);ma(first2);ma(dfn);ma(low);ma(cut);ma(c);ma(du);ma(dcc);
num_e=num_e2=n=num=top=cnt=0;
}
inline void add(int u,int v);
inline void add2(int u,int v);
signed main()
{
// freopen("in.txt","r",stdin); for(int ooo=1;;ooo++)
{
scanf("%lld",&m);if(!m)break;
init();
printf("Case %lld: ",ooo);
int a,b;
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&a,&b);
n=max(max(a,b),n);
add(a,b),add(b,a);
}
for(int i=1;i<=n;i++)
if(!dfn[i]){root=i;tarjan(i);}
if(cnt==1){printf("2 %lld\n",n*(n-1)/2);continue;}
num=cnt;
for(int i=1;i<=n;i++)
if(cut[i])c[i]=++num;
for(int i=1;i<=cnt;i++)
for(int j=0;j<dcc[i].size();j++)
{
int x=dcc[i][j];
if(cut[x])add2(i,c[x]),add(c[x],i),du[i]++;
else c[x]=i;
}
int ans=0,fa=1;
for(int i=1;i<=cnt;i++)
if(du[i]==1)
{
ans++;
fa*=dcc[i].size()-1;
}
printf("%lld %lld\n",ans,fa);
}
}
inline void add(int u,int v)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
n(num_e)=f(u);
f(u)=num_e;
}
inline void add2(int u,int v)
{
++num_e2;
u2(num_e2)=u;
v2(num_e2)=v;
n2(num_e2)=f2(u);
f2(u)=num_e2;
}
05-11 15:34