tarjan的题目一般扫一眼就看出来了
也可能是我太蒻了只会水水题
思路:tarjan求无向图的强连通分量
PS:求tarjan时顺便把割点求出来
与模板不同的是此题不能直接cout强连通分量的个数
要根据强连通分量的割点数量判断
在一个强连通分量(dcc[i])内:
1.设ans1为至少要修建的救援出口数量,ans2为方案总数
2.若割点数量>1,则对答案无影响
3.若割点数量=1,则有
ans1++;
ans2*=(dcc[i].size()-1);
4.若割点数量=0,则有
ans1+=2;
ans2*=dcc[i].size()*(dcc[i].size()-1)/2;
另外在判断第4点时,巨坑:方案总数每次要减半,因为有重复。
code如下
#include<bits/stdc++.h>
#define re register int
using namespace std;
int n,m,t,gs,cnt,root,dfn[50010],low[50010],cut[50010];
long long ans1,ans2=1;
vector<int>Edge[50010],dcc[50010];
stack<int>stk;
int read()
{
int ans=0;
char w=' ',ch=getchar();
while(ch<'0' || ch>'9')
{
w=ch;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
ans=(ans<<3)+(ans<<1);
ans+=ch-'0';
ch=getchar();
}
return (w=='-'?-ans:ans);
}
void tarjan(int x)
{
dfn[x]=low[x]=++t;
stk.push(x);
if(x==root && Edge[x].size()==0)
{
dcc[++cnt].push_back(x);
return;
}
int flag=0;
for(re j=0;j<Edge[x].size();j++)
{
int y=Edge[x][j];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
flag++;
if(x!=root || flag>1) cut[x]=1;
cnt++;
int z=0;
do
{
z=stk.top();
stk.pop();
dcc[cnt].push_back(z);
}while(z!=y);
dcc[cnt].push_back(x);
}
}
else
{
low[x]=min(dfn[y],low[x]);
}
}
}
int main()
{
int a1,a2;
while(cin>>m && m)
{
for(re i=1;i<=m;i++)
{
a1=read();a2=read();
Edge[a1].push_back(a2);
Edge[a2].push_back(a1);
n=max(n,max(a1,a2));
}
for(re i=1;i<=n;i++)
{
if(!dfn[i])
{
root=i;
tarjan(i);
}
}
for(re i=1;i<=cnt;i++)
{
int sum=0;
for(re j=0;j<dcc[i].size();j++)
{
if(cut[dcc[i][j]])
{
sum++;
}
}
if(sum==0)
{
ans1+=2;
ans2*=dcc[i].size()*(dcc[i].size()-1)/2;
}
else if(sum==1)
{
ans1++;
ans2*=(dcc[i].size()-1);
}
}
printf("Case %d: %lld %lld\n",++gs,ans1,ans2);
n=0;ans1=0;ans2=1;cnt=0;t=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(Edge,0,sizeof(Edge));
memset(dcc,0,sizeof(dcc));
memset(cut,0,sizeof(cut));
stk=stack<int>();
}
return 0;
}