【JSOI2010】联通数

题面

https://www.luogu.org/problem/P4306

题解

注意到一个强连通分量中的点是互相可达的,缩点形成的$DAG$的后继也是可达的,所以想到$dp$,但是因为不同的后继可能到达相同的后后继,所以用$bitset$判重。

要不是用了$bitset$我才不写呢。

注意$bitset$中,统计有多少个$1$用的是$b.count()$,并不是size

#include<stack>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#define ri register int
#define N 2050
using namespace std;

char s[N][N];
int n,dfn[N],low[N],cnt=0,clo,bel[N],siz[N];
bool ins[N],vis[N];
stack<int> sta;
vector<int> to[N],about[N];
bitset<N> ans[N];

void tarjan(int x) {
  dfn[x]=low[x]=++clo;
  sta.push(x); ins[x]=1;
  for (ri i=0;i<to[x].size();++i) {
    int y=to[x][i];
    if (dfn[y]) {
      if (ins[y]) low[x]=min(low[x],dfn[y]);
    }
    else {
      tarjan(y);
      low[x]=min(low[x],low[y]);
    }
  }
  if (low[x]==dfn[x]) {
    ++cnt;
    while (1) {
      int t=sta.top(); sta.pop();
      bel[t]=cnt; ins[t]=0; siz[cnt]++;
      ans[cnt][t]=1;
      if (t==x) break;
    }
  }
}

int main() {
  scanf("%d",&n);
  for (ri i=1;i<=n;i++) {
    scanf("%s",s[i]+1);
  }
  for (ri i=1;i<=n;i++) {
    for (ri j=1;j<=n;j++) if (s[i][j]=='1') to[i].push_back(j);
  }
  for (ri i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
  for (ri i=1;i<=n;i++) {
    for (ri j=1;j<=n;j++) if (s[i][j]=='1' && bel[i]!=bel[j]) about[bel[i]].push_back(bel[j]);
  }

  int sum=0;
  for (ri i=1;i<=cnt;i++) {
    for (ri j=0;j<about[i].size();j++) {
      ans[i]|=ans[about[i][j]];
    }
    sum+=siz[i]*ans[i].count();
  }
  printf("%d\n",sum);
}

 

posted @ 2019-08-27 21:11  HellPix  阅读(171)  评论(0编辑  收藏  举报