【传送门:BZOJ2730】
简要题意:
给出m条无向边,每个点有人,有时候会出现一个点崩塌,使得这个点和与这个点相连的边都不能经过
可以在某些点设置安全出口,其它可以到达这些点的点上的人可以逃出去
请问至少设置多少个安全出口和求出最少安全出口的情况下有多少种设置的方案
题解:
Tarjan求割点例题
设n为点数,n直接等于所有边中找出最大的点
题目中只会出现一个点崩塌
先找出所有割点,然后求出删除了割点之后的所有连通块与多少个割点相连
设num为安全出口数,ans为方案数,tot为当前连通块的点数
对于一个连通块
如果不与其它割点相连,num+=2,ans*=(tot-1)*tot/2
如果只与一个割点相连,num+=1,ans*=tot
如果与两个及以上割点相连,则不处理,因为即使其中一个割点崩塌,这个连通块的点依旧可以通过其它割点走向其它连通块的出口
特判一下,如果整个图都没有割点,则num=2,ans=n*(n-1)/2
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; struct node { int x,y,next; }a[1100];int len,last[510]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int dfn[510],low[510],id; bool v[510]; int rt,cnt; void findcut(int x) { dfn[x]=low[x]=++id; int t=0; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(dfn[y]==0) { t++; findcut(y); if(low[y]>=dfn[x]) v[x]=true,cnt++; } low[x]=min(low[x],low[y]); } if(x==rt&&t==1) v[x]=false,cnt--; } int tot; void dfs(int x,int t) { dfn[x]=t; if(v[x]==true){cnt++;return ;} tot++; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(dfn[y]!=t) { dfs(y,t); } } } int main() { int n,m;int T=0; while(scanf("%d",&m)!=EOF) { if(m==0) break;T++; len=0;memset(last,0,sizeof(last)); n=0; for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y);ins(y,x); n=max(n,max(x,y)); } id=cnt=0; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(v,false,sizeof(v)); for(int i=1;i<=n;i++) { rt=i; if(dfn[i]==0) findcut(i); } if(cnt==0) printf("Case %d: 2 %d ",T,n*(n-1)/2); else { id=0; memset(dfn,0,sizeof(dfn)); int num=0;LL ans=1; for(int i=1;i<=n;i++) { if(dfn[i]==0&&v[i]==false) { tot=cnt=0; dfs(i,i); if(cnt==0) num+=2,ans*=LL((tot-1)*tot/2); if(cnt==1) num+=1,ans*=LL(tot); } } printf("Case %d: %d %lld ",T,num,ans); } } return 0; }