图论3

· · 个人记录

floyd 进阶

用处1

直接求解多源最短路的长度(负边权,正边权)

用处2

判断任意两点的连通关系

这个还可以用 bfs 来做主要是看题目的数据范围

bfs$ 时间复杂度 $O(n \cdot m) floyd$ 时间复杂度 $O(n^3)

传递思路

x>y$ 且 $y>z

知道这些后,可以得到更多关系

x>y$ 且 $y>z$ ,说明 $x>z

时间复杂度

O(n^3)

模版

void floyd()
{
    for( int i = 1 ; i <= n ; i++ )
    {
        for( int j = 1 ; j <= n ; j++ )
        {
            for( int k = 1 ; k <= n ; k++ )
            {
                dis[j][k] |= (dis[j][i] & dis[i][k]) ;//括号里的必须同时满足,所以是&。前面部分只需满足一个,所以是|
            }
        }
    }
}

关于内存单位

$1byte=8bit

bitset 容器

## 用处 创建多个二进制位 ## 优点 1. 存储容量是 $bool$ 的 $\frac{1}{8}$ , $int$ 的 $\frac{1}{32}
  1. 多个位的位运算 bitset 更优

    缺点

    单个位的二进制位运算较慢

    定义

    bitset<200>bs ;//创建一个200位的二进制
    bitset<200>as ;//创建一个200位的二进制

创建了200位,25个字节, \approx 7int=25bool

原理

通过维护 01 的数量,减少 bool 的内存占用,时间也会相应的减少

注意

## 操作 ```cpp //支持各类位运算 bs | as ; bs << 1 ; //也会向int一样,自动溢出 //自带函数 bs.count() ; //bs串中1的个数 bs.size() ; //bs串的长度 bs.any() ; //是否有1 bs.set() ; //全改为1(可输出,输出的是现在的bs串) bs.reset() ; //全改为0(可输出,输出的是现在的bs串) (~bs) ; //每位取反 ``` # 例题 ## $floyd$ 进阶模块 ### [B3611 【模板】传递闭包](https://www.luogu.com.cn/problem/B3611) #### 题意 模板题,题意简单,无需解释 #### 分析 模板题,容易理解,无需分析 #### 注意 这题**没有**坑 #### 代码 ```cpp #include<bits/stdc++.h> using namespace std ; bool dis[105][105] ;// floyd 数组 int n ; void floyd() { for( int i = 1 ; i <= n ; i++ ) { for( int j = 1 ; j <= n ; j++ ) { for( int k = 1 ; k <= n ; k++ ) { dis[j][k] |= (dis[j][i] & dis[i][k]) ; } } } } int main() { cin >> n ; for( int i = 1 ; i <= n ; i++ ) { for( int j = 1 ; j <= n ; j++ ) { cin >> dis[i][j] ; } } floyd() ; for( int i = 1 ; i <= n ; i++ ) { for( int j = 1 ; j <= n ; j++ ) { cout << dis[i][j] << " " ; } cout << "\n" ; } return 0 ; } ``` ### [P2881 [USACO07MAR] Ranking the Cows G](https://www.luogu.com.cn/problem/P2881) #### 题意 有 $n$ 个奶牛, $m$ 个相对关系。其中 $m$ 个相对关系包含 $x$ 和 $y$ ,表示 $x$ 的产奶量比 $y$ 的产奶量大 #### 分析 和上题差不多用,就是统计时不一样 我们知道,要全都知道关系,需要知道每个的关系,就是说需要有 $n \times (n-1) \div 2$ 条边,所以答案就是 $n \times (n-1) \div 2$ 条边 $-$ 给出的矩阵中 $1$ 的个数 #### 注意 这题**没有**坑 #### 代码 ```cpp #include<bits/stdc++.h> using namespace std ; bool dis[1005][1005] ; int n , m ; void floyd() { for( int i = 1 ; i <= n ; i++ ) { for( int j = 1 ; j <= n ; j++ ) { for( int k = 1 ; k <= n ; k++ ) { dis[j][k] |= (dis[j][i] & dis[i][k]) ; } } } } int main() { cin >> n >> m ; for( int i = 1 ; i <= m ; i++ ) { int x , y ; cin >> x >> y ; dis[x][y] = 1 ;//这个点来过 } floyd() ; int cnt = n * (n - 1) / 2 ;//有这么多条边 for( int i = 1 ; i <= n ; i++ ) { for( int j = 1 ; j <= n ; j++ ) { cnt -= dis[i][j] ;//减掉1 } } cout << cnt ; return 0 ; } ``` #### $bitset$ 优化 你会发现前面的代码的时间很不乐观,不信,看[link](https://www.luogu.com.cn/record/173229131) 怎么优化呢? 可以用前面说到的 $bitset$ 容器,前面也说到了,他比 $bool$ 快 $8$ 倍 所以改完后: ```cpp #include<bits/stdc++.h> using namespace std ; bitset<1005> dis[1005] ; int n , m ; void floyd() { for( int i = 1 ; i <= n ; i++ ) { for( int j = 1 ; j <= n ; j++ ) { for( int k = 1 ; k <= n ; k++ ) { dis[j][k] = dis[j][k] | (dis[j][i] & dis[i][k]) ; } } } } int main() { cin >> n >> m ; for( int i = 1 ; i <= m ; i++ ) { int x , y ; cin >> x >> y ; dis[x][y] = 1 ; } floyd() ; int cnt = n * (n - 1) / 2 ; for( int i = 1 ; i <= n ; i++ ) { for( int j = 1 ; j <= n ; j++ ) { cnt -= dis[i][j] ; } } cout << cnt ; return 0 ; } ``` 自信评测! 。。。[link](luogu.com.cn/record/173229732) 你知道为什么说“单个二进制位运算较慢了吧” 继续修改: ```cpp #include<bits/stdc++.h> using namespace std ; bitset<1005> dis[1005] ; int n , m ; void floyd() { for( int i = 1 ; i <= n ; i++ ) { for( int j = 1 ; j <= n ; j++ ) { if(dis[j][i]) { dis[j] = dis[j] | dis[i] ; } } } } int main() { cin >> n >> m ; for( int i = 1 ; i <= m ; i++ ) { int x , y ; cin >> x >> y ; dis[x][y] = 1 ; } floyd() ; int cnt = n * (n - 1) / 2 ; for( int i = 1 ; i <= n ; i++ ) { cnt -= dis[i].count() ; } cout << cnt ; return 0 ; } ``` AC! [link](https://www.luogu.com.cn/record/173230054) 对比时间: $2.51s:57ms$ ,只想说句 $6