BZOJ从入门到入土

  1. [Jsoi2010]连通数:有向图求每一个点到能到达的点的个数的和(包括自己到自己)

SCC+bitset+dp

#include<bits/stdc++.h>
using namespace std;

// 方法:SCC + bitset传递闭包 或 直接bitset优化floyd
/**
 * 1. tarjan板子复习
 * 2. tarjan缩点重建图复习
 * 3. 利用bitset求传递闭包(更快,因为可以直接按位或)
 * 4. bitset优化floyd
 */

vector<int> h[2010]; // 原图的邻接表
// tarjan需要的变量
stack<int> stk;
int dfn[2010], low[2010], instk[2010], idx; 
int SCC[2010], cnt; // 每一个点所在的SCC编号、SCC的个数
int SIZE[2010]; // 每一个SCC中包含的点数

vector<int> scc[2010]; // 新图的邻接表
bitset<2010> g[2010]; // 用于求新图的传递闭包的bitset对于每一个点开一个bitset, 传递闭包直接或

void tarjan(int u){
    dfn[u] = low[u] = ++ idx;
    instk[u] = 1;
    stk.push(u);
    
    for(auto j : h[u]){
        if(!dfn[j]){
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }else if(instk[j] && dfn[j] < low[u])
            low[u] = dfn[j];
    }
    
    if(low[u] == dfn[u]){
        cnt ++;
        int num = 0;
        do{
            num ++;
            int t = stk.top();
            SCC[t] = cnt;
            instk[t] = 0;
            stk.pop();
            if(t == u) break;
        }while(1);
        SIZE[cnt] = num; // 当前SCC中包含的点数
    }
}

bitset<2010> dfs(int u){
    if(g[u] != 0) return g[u];
    g[u][u] = 1;
    for(auto v : scc[u]){
        g[u] |= dfs(v);
    }
    return g[u];
}

void solve(){
    int n;
    cin >> n;
    // 建立原图的邻接表
    for(int i = 0; i < n; i ++){
        for(int j = 0; j < n; j ++){
            char c;
            cin >> c;
            if(c == '1') h[i].push_back(j);
        }
    }
    
    // SCC
	for(int i = 0; i < n; i ++)
        if(dfn[i] == 0) tarjan(i);
   
    // 重建图,需要遍历所有边
    for(int i = 0; i < n; i ++){
        for(int j = 0; j < h[i].size(); j ++){
            if(SCC[i] != SCC[h[i][j]]) scc[SCC[i]].push_back(SCC[h[i][j]]);
        }
    }
    
    int ans = 0;
    for(int i = 1; i <= cnt; i ++){ // SCC是从1开始编号的
        auto k = dfs(i); // 求从i出发能到达的点的bitset
        for(int j = 1; j <= cnt; j ++) // 遍历bitset
            if(k[j])
                ans += SIZE[i] * SIZE[j];
    }
    
    cout << ans << endl;
}

signed main(){
    solve();
}

bitset+floyd

#include<bits/stdc++.h>
using namespace std;

bitset<2010> g[2010];

void solve(){
    int n;
    cin >> n;
    for(int i = 0; i < n; i ++){
        string s;
        cin >> s;
        reverse(s.begin(), s.end());
        g[i] = bitset<2010>(s);
        g[i][i] = 1;
    }
    
    for(int k = 0; k < n; k ++)
        for(int i = 0; i < n; i ++)
            if(g[i][k]) g[i] |= g[k];
    
    int ans = 0;
    for(int i = 0; i < n; i ++){
        ans += g[i].count();
    }
    
    cout << ans << endl;
}

signed main(){
    solve();
}
posted @ 2021-11-29 11:18  yys_c  阅读(54)  评论(0编辑  收藏  举报