Description
Input
输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。
Output
输出一行一个整数,表示该图的连通数。
Sample Input
3
010
001
100
010
001
100
Sample Output
9
HINT
对于100%的数据,N不超过2000。
原来我想的是用树形dp搞,但是发现会重复统计
比如3个点3条边,1->2,2->3,1->3,先建反边按拓扑排序的顺序做:3更新1、2,但是2更新1的时候1已经统计过了3,所以重复记数
考虑状压一下,保存每个点能到达的点,更新的时候就不会重复统计了
说到状压,必须讲下bitset大法好!
当然缩点还是省不了的
#include<cstdio> #include<iostream> #include<bitset> #define LL long long #define N 2010 using namespace std; inline LL read() { LL x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,cnt,cnt2,cnt3,tt,ans; bitset <N> flag[N]; char ch[N]; struct edge{int to,next;}e[N*N],e2[N*N]; int head[N],head2[N],dfn[N],low[N]; int belong[N],size[N],son[N]; int zhan[N],top,q[N]; bool inset[N],mrk[N]; int I[N],O[N]; inline void insert(int u,int v) { e[++cnt].to=v; e[cnt].next=head[u]; head[u]=cnt; } inline void ins(int u,int v) { e2[++cnt2].to=v; e2[cnt2].next=head2[u]; head2[u]=cnt2; } inline void dfs(int x) { dfn[x]=low[x]=++tt; zhan[++top]=x;inset[x]=1; for (int i=head[x];i;i=e[i].next) if (!dfn[e[i].to]) { dfs(e[i].to); low[x]=min(low[x],low[e[i].to]); }else if (inset[e[i].to]) low[x]=min(low[x],dfn[e[i].to]); if (dfn[x]==low[x]) { int p=-1; cnt3++; while (p!=x) { p=zhan[top--]; belong[p]=cnt3; inset[p]=0; size[cnt3]++; } } } inline void tarjan() { for (int i=1;i<=n;i++) if (!dfn[i])dfs(i); } int main() { n=read(); for (int i=1;i<=n;i++) { scanf("%s",ch+1); for (int j=1;j<=n;j++) if (ch[j]=='1')insert(i,j); } tarjan(); for (int i=1;i<=n;i++) for (int j=head[i];j;j=e[j].next) if (belong[e[j].to]!=belong[i]) { ins(belong[e[j].to],belong[i]); flag[belong[i]][belong[e[j].to]]=1; I[belong[i]]++; O[belong[belong[e[i].to]]]++; } int t=0,w=0; for (int i=1;i<=cnt3;i++) { flag[i][i]=1; if (!I[i])q[w++]=i,mrk[i]=1; } while (t!=w) { int now=q[t++]; for (int i=head2[now];i;i=e2[i].next) { int to=e2[i].to; if (mrk[to])continue; flag[to]|=flag[now]; I[to]--; if (!I[to]) { mrk[to]=1; q[w++]=to; } } } for (int i=1;i<=cnt3;i++) for (int j=1;j<=cnt3;j++) if (flag[i][j])ans+=size[i]*size[j]; printf("%d\n",ans); return 0; }
——by zhber,转载请注明来源