AcWing 164. 可达性统计

Posted ak-ls

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AcWing 164. 可达性统计相关的知识,希望对你有一定的参考价值。

给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。

输入格式

第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。

输出格式

输出共N行,表示每个点能够到达的点的数量。

数据范围

1N,M30000


显然可以用拓扑排序+状态压缩来做, 用一个n位的二进制数存每一个f[x], 其中第i位是1表示x能到i,0则不能到i, 这样就相当于存在x 到 y的一条边,f[x] |= f[y], 再预处理处拓扑序, 反向枚举, 最后判断每个f[i]中的个数, 但有一个bug就是, 就算unsigned long long 二进制下只有64位, 这里就有一个小小的干货:

 

关于状态压缩 bitset容器:
转载:https://oi-wiki.org/ds/stl/bitset/
介绍¶
std :: bitset 是标准库中的一个固定大小序列,其储存的数据只包含 0/1

 

众所周知,由于内存地址是按字节即 byte 寻址,而非比特 bit ,

 

我们一个 bool 类型的变量,虽然只能表示 0/1 , 但是也占了 1byte 的内存

 

bitset 就是通过固定的优化,使得一个字节的八个比特能分别储存 8 位的 0/1

 

对于一个 4 字节的 int 变量,在只存 0/1 的意义下, bitset 占用空间只是其

 

在某些情况下通过 bitset 可以使你的复杂度除以 32

 

当然, vector 的一个特化 vector<bool> 的储存方式同 bitset 一样,区别在于其支持动态开空间,

 

bitset 则和我们一般的静态数组一样,是在编译时就开好了的。

 

那么为什么要用 bitset 而非 vector<bool> ?

 

通过以下的介绍,你可以更加详细的看到 bitset 具备的方便操作

 

#include <bitset> // 包含 bitset 的头文件
运算符¶
operator[] : 访问其特定的一位

 

operator ==/!= : 比较两个 bitset 内容是否完全一样

 

operator &=/|=/^=/~ : 进行按位与/或/异或/取反操作

 

operator <</>>/<<=/>>= : 进行二进制左移/右移

 

operator <</>> : 流运算符,这意味着你可以通过 cin/cout 进行输入输出

 

vector<bool> 只具有前两项

 

成员函数¶
test() : 它和 vector 中的 at() 的作用是一样的,和 [] 运算符的区别就是越界检查
count() : 返回 true 的数量
set() : 将整个 bitset 设置成 true , 你也可以传入参数使其设置成你的参数
reset() : 将整个 bitset 设置成 false
flip() : 翻转该位 (0 变 1,1 变 0), 相当于逻辑非/异或 1
to_string() : 返回转换成的字符串表达
to_ulong() : 返回转换成的 unsigned long 表达 ( long 在 NT 及 32 位 POSIX 系统下与 int 一样,在 64 位 POSIX 下与 long long 一样)
to_ullong() C++11, 返回转换成的 unsigned long long 表达
这些 vector<bool> 基本都没有

 

作用¶
一般来讲,我们可以用 bitset 优化一些可行性 DP, 或者线筛素数 ( notprime 这种 bool 数组可以用 bitset 开到 之类的)

 

它最主要的作用还是压掉了内存带来的时间优化, 的常数优化已经可以是复杂度级别的优化了,比如一个 的 算法, 显然很卡,在常数大一点的情况下必然卡不过去,O(松)不能算!, 这时候如果我们某一维除以 32, 则可以比较保险的过了这道题

 

其实 bitset 不光是一个容器,更是一种思想,我们可以通过手写的方式,来把 long long 什么的压成每 bit 表示一个信息,用 STL 的原因更多是因为它的运算符方便

作者:Chicago
链接:https://www.acwing.com/solution/acwing/content/899/
来源:AcWing

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 3e4 + 100;
const int MAXM = 3e3 + 10;
const double eps = 1e-5;

template < typename T > inline void read(T &x) 
    x = 0; T ff = 1, ch = getchar();
    while(!isdigit(ch)) 
        if(ch == -) ff = -1;
        ch = getchar();
    
    while(isdigit(ch)) 
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar(); 
    
    x *= ff;



template < typename T > inline void write(T x) 
    if(x < 0) putchar(-), x = -x;
    if(x > 9) write(x / 10);
    putchar(x % 10 + 0);


int n, m, top, in[MAXN], b[MAXN];
bitset < MAXN > a[MAXN];
int lin[MAXN], tot = 0;
struct edge 
    int y, next;
e[MAXN];

inline void add(int xx, int yy) 
    e[++tot].y = yy;
    e[tot].next = lin[xx];
    lin[xx] = tot;


void topsort() 
    queue < int > q;
    for(int i = 1; i <= n; ++i) 
        if(in[i] == 0) q.push(i);
     
    while(!q.empty()) 
        int x = q.front(); q.pop();
        b[++top] = x;
        for(int i = lin[x], y; i; i = e[i].next) 
            if(--in[y = e[i].y] == 0) q.push(y);                                                        
         
    
    


int main() 
    read(n); read(m);
    for(int i = 1; i <= m; ++i) 
        int x, y;
        read(x); read(y);
        add(x, y);
        in[y]++;
     
    topsort();
    for(int i = top; i >= 1; --i) 
        int x = b[i];
        a[x][x] = 1;
        for(int j = lin[x]; j; j = e[j].next) 
            a[x] |= a[e[j].y]; 
        
     
    for(int i = 1; i <= n; ++i) 
        cout << a[i].count() << endl;
    
    
    return 0;

 

以上是关于AcWing 164. 可达性统计的主要内容,如果未能解决你的问题,请参考以下文章

AcWing 164.可达性统计(图论+拓扑排序+位运算)

AcWing 164.可达性统计(图论+拓扑排序+位运算)

AcWing 164.可达性统计(图论+拓扑排序+位运算)

AcWing 164.可达性统计(图论+拓扑排序+位运算)

AcWing 164. 可达性统计 拓扑排序+状态压缩

AcWing:164. 可达性统计(拓扑排序 + 状态压缩算法)