bzoj 2730 割点
首先我们知道,对于这张图,我们可以枚举坍塌的是哪个点,对于每个坍塌的点,最多可以将图分成若干个不连通的块,这样每个块我们可能需要一个出口才能满足题目的要求,枚举每个坍塌的点显然是没有意义的,我们只需要每个图的若干个割点,这样除去割点的图有若干个块,我们可以求出只与一个割点相连的块,这些块必须要一个出口才能满足题目的要求,每个块内有块内个数种选法,然后将所有满足一个割点相连的块的点数连乘就行了。
对于每个与一个割点相连的块必须建出口可以换一种方式理解,我们将每个块看做一个点,那么算上割点之后,这张图就变成了一颗树,只有叶子节点我们需要建立出口,因为对于非叶子节点我们不论断掉哪个点我们都有另一种方式相连,这里的叶子节点就是与一个割点相连的块。
最后还有个特判,就是对于一个双连通图,我们至少需要选取两个点作为出口,因为如果就选一个,可能该点为坍塌点,这时我们就任选两个点就行了,方案数为点数*(点数-1)>>1。
反思:最开始的时候求只与一个割点相连的块的计算的时候割点计算重复了,后来没有割点的时候的特判没有加,最后的时候发现这道题需要开long long,开了之后忘记改输出的通配符,改了之后输出2的时候应该是2ll,对C++的使用还不够熟悉。
/************************************************************** Problem: 2730 User: BLADEVIL Language: C++ Result: Accepted Time:40 ms Memory:2956 kb ****************************************************************/ //By BLADEVIL #include <cstdio> #include <cstring> #define maxn 50010 using namespace std; int n; int last[maxn],pre[maxn],other[maxn]; int dfn[maxn],low[maxn],cut[maxn],vis[maxn],size[maxn],num[maxn],flag[maxn],fuck[maxn]; int l,time; long long ans1,ans2,task; void getmin(int &x,int y) {if (y<x) x=y;} void connect(int x,int y) { pre[++l]=last[x]; last[x]=l; other[l]=y; } void dfs(int x,int fa) { low[x]=dfn[x]=++time; int q,p,cnt=0; for (q=last[x];q;q=pre[q]) { p=other[q]; if (p==fa) continue; if (!dfn[p]) { dfs(p,x); cnt++; getmin(low[x],low[p]); if (dfn[x]<=low[p]&&fa!=-1) cut[x]=1; } else getmin(low[x],dfn[p]); } if (fa==-1&&cnt>1) cut[x]=1; } void make(int x,int fa) { int p; for (int q=last[x];q;q=pre[q]) { p=other[q]; if (p==fa||cut[p]) continue; if (!vis[p]) vis[p]=vis[x],make(p,x); } } void solve() { int m=n; n=0; while (m--) { int x,y; scanf("%d%d",&x,&y); connect(x,y); connect(y,x); n=(x>n)?x:n; n=(y>n)?y:n; fuck[x]=fuck[y]=1; } for (int i=1;i<=n;i++) if (!dfn[i]) dfs(i,-1); //for (int i=1;i<=n;i++) if (cut[i]) printf("%d ",i); for (int i=1;i<=n;i++) if (!(vis[i]||cut[i])) vis[i]=i,make(i,-1); //for (int i=1;i<=n;i++) printf("%d ",vis[i]); printf("\n"); for (int i=1;i<=n;i++) if (vis[i]) size[vis[i]]++; for (int i=1;i<=n;i++) if (cut[i]) { memset(flag,0,sizeof flag); for (int q=last[i];q;q=pre[q]) if (!flag[vis[other[q]]]) flag[vis[other[q]]]=1,num[vis[other[q]]]++; } //for (int i=1;i<=n;i++) printf("%d %d %d\n",i,size[i],num[i]); for (int i=1;i<=n;i++) if (num[i]==1) ans1++,ans2*=size[i]; if (!ans1) { ans2=0; for (int i=1;i<=n;i++) if (fuck[i]) ans2++; } if (!ans1) printf("Case %lld: %lld %lld\n",task,2ll,ans2*(ans2-1)>>1); else printf("Case %lld: %lld %lld\n",task,ans1,ans2); } void clear() { time=l=ans1=0ll; ans2=1ll; memset(last,0,sizeof last); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(cut,0,sizeof cut); memset(vis,0,sizeof vis); memset(size,0,sizeof size); memset(num,0,sizeof num); memset(fuck,0,sizeof fuck); } int main() { scanf("%d",&n); while (n) { task++; clear(); solve(); scanf("%d",&n); } return 0; }