可达性统计 拓扑排序+bitset

题目链接

题目描述

输入样例:

10 10
3 8
2 3
2 5
5 9
5 9
2 3
3 9
4 8
2 10
4 9

输出样例:

1
6
3
3
2
1
1
1
1
1

题目思路

给你一个有向无环图 ,让你去统计每个点可到达点的数量
在分析一下题目数据范围,如果暴力去找的话,肯定要TLE;
既然是有向无环图DAG , 这提示我们可以去拓扑排序;

假设上图中的一个拓扑序列,设F[x] 是x点能够 到达的点 的 集合,不难发现
F[5] = {5}
F[4] = {4} ∪ F[5];
F[3] = {3} ∪( F[4])
即 F[x] = 自身的点 + F[y]【存在若干有向边(x,y)】 F[y] = F[y1] 并 F[y2] 并 --- 并 F[yn]
由于拓扑排序 , 一定满足有向边(x,y) x在y的前面,所以对拓扑序列倒叙求一下F,便可完成对可达点的计数
如何优化空间与时间求F那?
我们用状态压缩的方法,用一个二进制数将 所有的点的 可达与不可达 用 1,0 表示;
这样一来对若干个集合求并集,相当于我的二进制数求 | (或) 运算 ,最后神奇的发现 F(x) 二进制中1的数量就是 我们想要的答案

这里我们可以用STL中的bitset 容器

bitset

是标准库中的一个固定大小序列,其储存的数据只包含 0/1

内存地址是按字节即 byte 寻址,而非比特 bit ,

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

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

对于一个 4 字节的 int 变量,在只存 0/1 的意义下, bitset 占用空间可以使你的复杂度除以 32

bitset成员函数


对于一个叫做foo的bitset:
foo.size() 返回大小(位数)
foo.count() 返回1的个数
foo.any() 返回是否有1
foo.none() 返回是否没有1
foo.set() 全都变成1
foo.set(p) 将第p + 1位变成1
foo.set(p, x) 将第p + 1位变成x
foo.reset() 全都变成0
foo.reset(p) 将第p + 1位变成0
foo.flip() 全都取反
foo.flip(p) 将第p + 1位取反
foo.to_ulong() 返回它转换为unsigned long的结果,如果超出范围则报错
foo.to_ullong() 返回它转换为unsigned long long的结果,如果超出范围则报错
foo.to_string() 返回它转换为string的结果

代码

#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
#include <bitset>
using namespace std;
const int N = 3e4+5;
int n,m,tol,head[N],d[N];
bitset<N> f[N];
struct node{
    int to,next;
}edge[N];
void add(int u , int v)
{
    edge[tol].to = v;
    edge[tol].next = head[u];
    head[u] = tol ++;
}
vector<int>v1;
void topsort()
{   queue<int>q;
    for ( int i = 1; i <= m; i ++ )
    {
        if(d[i] == 0)
        q.push(i);
    }
    while(!q.empty())
    {
        int v = q.front();
        q.pop();
        v1.push_back( v );
        for ( int i = head[v]; i != -1; i = edge[i].next )
        {
            node e = edge[i];
            d[e.to] --;
            if(d[e.to] == 0)
            {
                q.push(e.to);
            }
        }
    }


}
int main()
{
    cin >> n >> m;
    tol = 0;
    memset(head, -1, sizeof head);
    for ( int i = 1; i <= m; i ++ )
    {
        int u , v;
        cin >> u >> v;
        add( u , v);
        d[v] ++;

    }
    topsort();
    for (int i = v1.size()-1 ; i >= 0 ; i-- )
    {
        int x = v1[i];
        f[x][x] = 1;
        for (int j = head[x] ; j != -1 ; j = edge[j].next)
        {
            node  e = edge[j];
            f[x] |= f[e.to];
        }
    }
    for ( int i = 1 ; i <= n ; i ++)
    cout<<f[i].count()<<endl;

}


posted @ 2021-08-05 10:07  Xiaomostream  阅读(63)  评论(0编辑  收藏  举报