1 条题解

  • 0
    @ 2026-6-13 21:27:21

    // SCC缩点+拓扑排序+bitset O(N)
    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=2010;
    vector<int> e[N],ee[N];
    int n,m,in[N],ans;
    int dfn[N],low[N],stk[N],top,scc[N],siz[N],cnt;
    bitset<N> f[N]; //f[i]表示i这个点的可以抵达点的数量,即二进制中1的个数
    
    void tarjan(int x){ //SCC缩点
      dfn[x]=low[x]=++dfn[0]; stk[++top]=x;
      for(auto y:e[x]){
        if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
        else if(!scc[y]) low[x]=min(low[x],dfn[y]); 
      }
    
      if(dfn[x]==low[x]){
        ++cnt;
        while(stk[top+1]!=x) scc[stk[top--]]=cnt,siz[cnt]++;
      }
    }
    void topo(){
      queue<int> q;
      for(int i=1; i<=cnt; i++) if(!in[i]) q.push(i);
      
      while(!q.empty()){
        int u=q.front(); q.pop();
        f[u][u]=1; //可以到达自己
        for(auto v:ee[u]){
          f[v]|=f[u]; //非重复统计用并集运算
          if(--in[v]==0) q.push(v);
        }
      }
    }
    int main(){
      cin>>n;
      for(int i=1;i<=n;i++){
        string s; cin>>s;
        for(int j=0;j<=s.size();j++) 
          if(s[j]=='1') e[i].push_back(j+1); //连边
      }
      
      for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); //SCC缩点
      
      for(int i=1;i<=n;i++)for(int j:e[i]) //枚举原始点的临接点
        if(scc[j]!=scc[i]) ee[scc[j]].push_back(scc[i]),in[scc[i]]++;
            
      topo(); //对缩点拓扑DP
      
      for(int i=1;i<=cnt;i++)for(int j=1;j<=cnt;j++) //任意两个缩点(包括自身)
        if(f[i][j]) ans+=siz[i]*siz[j]; //如果可达,缩点之内的点必然可达
      cout<<ans;
    }
    
    • 1

    D161 SCC 缩点+拓扑+bitset Tarjan 算法[JSOI2010] 连通数

    信息

    ID
    3873
    时间
    300ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    2
    已通过
    2
    上传者