bzoj2730: [HNOI2012]矿场搭建
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2730
思路:点双缩点
对于一个点双,如果它不与任何一个割点相连,那它就要建两个出口
与一个相连,就见一个
与两个以上相连就不用建
方案就是每个点双去掉割点的点数之积
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int maxn=1010,maxm=maxn<<1; typedef long long ll; using namespace std; int n,m,now[maxn],son[maxm],pre[maxm],T; int low[maxn],dfn[maxn],tim,tot,cas,deg,cnt,num,vis[maxn],ans1,root;bool cut[maxn]; ll ans2; void clear(){ memset(now,0,sizeof(now)),memset(low,0,sizeof(low)),memset(dfn,0,sizeof(dfn)); memset(cut,0,sizeof(cut)),memset(vis,0,sizeof(vis)),tot=n=tim=T=ans1=0,cas++,ans2=1; } void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;} void tarjan(int x,int fa){ dfn[x]=low[x]=++tim;//printf("%d\n",x); for (int y=now[x];y;y=pre[y]){ if (son[y]==fa) continue; if (!dfn[son[y]]) tarjan(son[y],x),low[x]=min(low[x],low[son[y]]); else{low[x]=min(low[x],dfn[son[y]]);continue;} //printf("fuckpp%d %d\n",x,son[y]); if (dfn[x]<=low[son[y]]){if (x==root) deg++;else cut[x]=1;} } } void dfs(int x){ //printf("%d\n",x); vis[x]=T;if (cut[x]) return; cnt++; for (int y=now[x];y;y=pre[y]){ if (cut[son[y]]&&vis[son[y]]!=T) num++,vis[son[y]]=T; if (!vis[son[y]]) dfs(son[y]); } } int main(){ scanf("%d",&m); while (m){ clear(); for (int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x),n=max(x,n),n=max(n,y); for (int i=1;i<=n;i++){ if (!dfn[i]){tarjan(root=i,0);if (deg>=2) cut[root]=1;}//root要特判,因为要有两个才算割点 deg=0; } //for (int i=1;i<=n;i++) {printf("%d %d %d\n",i,dfn[i],low[i]);if (cut[i]) printf("%d\n",i);} for (int i=1;i<=n;i++) if (!vis[i]&&!cut[i]){ ++T,cnt=num=0,dfs(i);//T时间戳,cnt:块的大小,num:块中割点数 if (!num) ans1+=2,ans2*=cnt*(cnt-1)/2; if (num==1) ans1++,ans2*=cnt; } printf("Case %d: %d %lld\n",cas,ans1,ans2); scanf("%d",&m); } return 0; }