• 连通数


    LuoguP4306    bzoj2208

    这道题里面还有缩点+拓扑排序

    话不多说直接上代码,代码里有详细解释,看不懂的先去看看搜索树方面的知识,lyd书上有

    Code:

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 int n, ans, cnt, sum, tot;//tot记录图中有几个强连通分量
      4 bool v[2021];//判断节点是否被访问过
      5 int dfn[2021];//节点i搜索的次序编号(时间戳)每个点第一次被访问的时间顺序
      6 int low[2021];//表示u或u的子树能够追溯到的最早的栈中节点的次序号,时间戳的最小值
      7 int scc[2021];//表示 x 所在的强连通分量的编号
      8 int deg[2021];//储存强连通分量的入度
      9 int head[2021];//一开始的图
     10 int head2[2021];//保存缩点后新的有向无环图
     11 int stk[2021], top;//数组模拟栈,top记录栈中元素个数
     12 int sze[2021];//记录每个强连通分量中元素个数
     13 bitset<150021> f[100021];
     14 queue<int> q;
     15 struct edge{
     16     int u, v;
     17 }no[2021*2021], no2[2021*2021];
     18 void add(int from, int to) {
     19     no[++cnt].u = to;
     20     no[cnt].v = head[from];
     21     head[from] = cnt;
     22 }
     23 void add2(int from, int to) {
     24     no2[++cnt].u = to;
     25     no[cnt].v = head[from];
     26     head2[from] = cnt;
     27 }
     28 void tarjan(int now) {
     29     dfn[now] = low[now] = ++sum;//初始化为自己,时间戳++
     30     stk[++top] = now;//入栈
     31     v[now] = true;//标记为已访问过
     32     for (int i = head[now]; i; i = no[i].v) {
     33         int to = no[i].u;
     34         if (!dfn[to]) {//如果该点没被访问过,则该边可以加到搜索树中
     35             tarjan(to);
     36             low[now] = min(low[now], low[to]);
     37         } else if (v[to]) {//如果以被访问过,则该边不能被加到搜索树中
     38             low[now] = min(low[now], dfn[to]);
     39         }
     40     }
     41     if (low[now] == dfn[now]) {// 如果节点now是强连通分量的根
     42                                //则以该节点为根的子树中所有节点不可能与栈中元素构成环
     43                                //此时从now到栈定的所有节点构成一个强连通分量
     44         int y;
     45         tot++;//强连通分量个数++
     46         do {
     47             y = stk[top--];//弹出栈顶元素,为一个强连通分量的一个元素
     48             scc[y] = tot;//记录编号,该元素属于编号为tot的强连通分量
     49             sze[tot]++;//该连通块中元素++
     50             v[y] = false;
     51         } while (y != now);//直到该节点为止
     52     }
     53 }
     54 int main () {
     55     scanf("%d", &n);
     56     for (int i = 1; i <= n; i++) {
     57         for (int j = 1; j <= n; j++) {
     58             int x;
     59             scanf("%1d", &x);
     60             if (x == 1) {
     61                 add(i, j);
     62             }
     63         }
     64     }
     65     cnt = 0;
     66     for (int i = 1; i <= n; i++) {
     67         if (!dfn[i]) {
     68             tarjan(i);
     69         }
     70     }
     71     for (int x = 1; x <= n; x++) {//缩点
     72         for (int i = head[x]; i; i = no[i].v) {
     73             int to = no[i].u;
     74             if (scc[x] == scc[to]) {//共属同一强连通分量
     75                 continue;
     76             }
     77             deg[scc[x]]++;// scc[x] 表示的强连通分量入度++
     78             add2(scc[to], scc[x]);
     79         }
     80     }
     81     for (int i = 1; i <= tot; i++) {
     82         f[i][i] = 1;//自己到自己也算联通
     83     }
     84     for (int i = 1; i <= tot; i++) {//拓扑排序
     85         if (!deg[i]) {//说明 i 入度为0
     86             q.push(i);//入队,将其分离
     87         }
     88     }
     89     while (q.size()) {//拓扑排序,直到所有点被分离出来
     90         int u = q.front();
     91         q.pop();
     92         for (int i = head2[u]; i; i = no2[i].v) {
     93             int to = no2[i].u;
     94             deg[to]--;//该点指向的点入度--
     95             f[to] |= f[u];//或运算累加
     96             if (!deg[to]) {
     97                 q.push(to);
     98             }
     99         }
    100     }
    101     for (int i = 1; i <= tot; i++) {
    102         for (int j = 1; j <= tot; j++) {
    103             if (f[i][j]) {//bitset表示两强连通分量是否联通
    104                 ans += sze[i] * sze[j];//个数相乘
    105             }
    106         }
    107     }
    108     printf("%d
    ", ans);
    109     return 0;
    110 }
    111 
    112 /*   bitset 做法   B君的
    113 #include <bits/stdc++.h>
    114 using namespace std;
    115 bitset<2000>d[2000];
    116 char s[2020];
    117 int n, z;
    118 int main() {
    119     scanf("%d", &n);
    120     for (int i = 0; i < n; i++) {
    121         scanf("%s", s);
    122         for (int j = 0; j < n; j++) {
    123             if (s[j] == '1') {
    124                 d[i][j] = 1;
    125             }
    126         }
    127         d[i][i] = 1;
    128     }
    129     for (int k = 0; k < n; k++) {
    130         for (int i = 0; i < n; i++) {
    131             if (d[i][k]) {
    132                 d[i] |= d[k];
    133             }
    134         }
    135     }
    136     for (int i = 0; i < n; i++) {
    137         z += d[i].count();
    138     }
    139     printf("%d
    ", z);
    140 }
    141 */
    View Code
  • 相关阅读:
    php框架laravel:数据库建立:artisan
    SpringCloud微服务(03):Hystrix组件,实现服务熔断
    SpringCloud微服务(02):Ribbon和Feign组件,实现服务调用的负载均衡
    SpringCloud微服务(01):Eureka组件,管理服务注册与发现
    SpringBoot2基础,进阶,数据库,中间件等系列文章目录分类
    Java描述设计模式(04):抽象工厂模式
    Java描述设计模式(03):工厂方法模式
    Java描述设计模式(02):简单工厂模式
    Java描述设计模式(01):单例模式
    SpringBoot2.0 整合 SpringSecurity 框架,实现用户权限安全管理
  • 原文地址:https://www.cnblogs.com/Sundial/p/11830373.html
Copyright © 2020-2023  润新知