bzoj2730: [HNOI2012]矿场搭建
容易看出是双联通。
and then??
割点!!终于有题证明了low[x]=min(low[x],dfn[y]);不能改成low[y]了
然后不会做(对强联通理解不够深刻)
先把每个联通块割点数弄出来,再找一次每个联通块
#include<cstdio> #include<iostream> #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 z,low[510],dfn[510],cut[510]; void findcut(int x) { dfn[x]=low[x]=++z; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(dfn[y]==0) { findcut(y); low[x]=min(low[x],low[y]); if(low[y]>=dfn[x])cut[x]++; } else low[x]=min(low[x],dfn[y]); } } int ans1;LL ans2; int top,sta[510]; void findans(int x) { dfn[x]=low[x]=++z; sta[++top]=x; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(dfn[y]==0) { findans(y); low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]) { int i;int cc=0,tot=0; do { i=sta[top];top--; if(cut[i]>=2)cc++; tot++; }while(i!=y); i=x; if(cut[i]>=2)cc++; tot++; if(cc==0) ans1+=2, ans2*=tot*(tot-1)/2; if(cc==1) ans1++, ans2*=tot-1; } } else low[x]=min(low[x],dfn[y]); } } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int n,m,x,y,ttt=0; while(scanf("%d",&m)!=EOF) { if(m==0)break;ttt++; n=0;len=0;memset(last,0,sizeof(last)); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); ins(x,y);ins(y,x); n=max(n,max(x,y)); } z=0; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(cut,0,sizeof(cut)); for(int i=1;i<=n;i++) if(dfn[i]==0)findcut(i); else cut[i]++; z=0; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); ans1=0;ans2=1; for(int i=1;i<=n;i++) if(dfn[i]==0)findans(i); printf("Case %d: %d %lld\n",ttt,ans1,ans2); } return 0; }
pain and happy in the cruel world.