【图论·强连通分量scc】hdu6072Logical Chain

非常有灵性的一道强连通分量。乍一看是道模板题,然而需要用bitset优化时间复杂度至1/64;bitset什么鬼啊喂,很佩服赛场上唯一做出来的大佬orz;

常规部分,学习了scc模板求强连通分量;
第一遍dfs给图上的倒序编号(最后访问到的标最小号);
第二遍在所有边反向后的图上dfs,从一个强连通分图上的点不可能访问到其他强分图上的点(为什么还没有理解)
cmp[I] = k 表示编号为i的点属于编号为k的强分图;
scc结束;

本题中节点数为t的强分图,对答案贡献t*(t-1)/2;

难点就是bitset 的理解和使用了(紧张);
首先试了下头文件#include
然而长度为8的bitset<8> b; 只能放8个0/1;
并不是很神奇;遂放弃,看大神的手写版;

这就非常有灵性了。
reset(int x) 好理解,初始化。x=0 b为0,x = 0xff,无限大;
flip(int x) 不好理解。8位,256个数,把x这个数0变1,1变0;
b[x>>5] 每一位表示32个数,x>>5确定x属于哪一位管;
将这一位加上2的x%32次方,表示这个数的存在

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<stack>
#include<queue>
#include<bitset>
using namespace std;
using std::bitset;
typedef long long ll;
const int maxv = 255;

    struct BIT{
        unsigned int b[8];
        void reset(int x){ memset(b, x, sizeof(b)); }
        void flip(int x){ b[x >> 5] ^= 1U << (x & 31); }
        int get(int x){ return (b[x >> 5] >> (x & 31)) & 1; }
    }G[maxv], rG[maxv], vis;

int V;
vector<int> vs;
int cmp[maxv];
int cnt = 0;

inline int ctz(unsigned int s)
{
    int x = 0;
    while (!(s & 1))
    {
        ++x; s >>= 1;
    }
    return x;
}

void dfs(int v){
    vis.flip(v);
    unsigned int s;
    for (int i = 0; i < 8; ++i){
        while (1){
             s = vis.b[i] & G[v].b[i];
            if (!s) break;
            //这里是什么原理依然没有搞清楚orz;
            dfs((i << 5)| ctz(s));  
        }
    }
    vs.push_back(v);
}

void rdfs(int v){
    ++cnt;
    vis.flip(v);
    unsigned int s;
    for (int i = 0; i < 8; i++){
        while (1){
            s = vis.b[i] & rG[v].b[i];
            if (!s) break;
            rdfs((i << 5 )| ctz(s));
        }
    }
}
void scc(){
    vis.reset(0xff);vs.clear();
    for (int i = 0; i < V; i++){
        if (vis.get(i)) dfs(i);
    }
    vis.reset(0xff);
    ll ans = 0;
    for (int i = vs.size() - 1; i >= 0; i--){
        if (vis.get(vs[i])) {
            cnt = 0;
            rdfs(vs[i]);
            ans += cnt*(cnt - 1) / 2;
        }
    }
    printf("%lld\n", ans);
}

char c;
int t, m;
int main(){
    scanf("%d", &t);
    while (t--){
        scanf("%d%d", &V, &m);
        for (int i = 0; i <= V; i++){
            G[i].reset(0);
            rG[i].reset(0);
        }
        for (int i = 0; i < V; i++){
            char g[maxv];
            scanf("%s", g);
            for (int j = 0; j < V; j++){
                if (g[j] == '1') {
                    G[i].flip(j);
                    rG[j].flip(i);
                }
            }
        }

        while (m--){
            int k; scanf("%d", &k);
            for (int i = 1; i <= k; ++i){
                int u, v;

        scanf("%d%d", &u, &v);
                G[u - 1].flip(v - 1);
                rG[v - 1].flip(u - 1);
            }
            scc();
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值