164. 可达性统计

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

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

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

1≤N,M≤30000

输入样例:

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

这个题需要先了解一下bitset,可以参考下面这篇博客。
参考博客:https://blog.csdn.net/qll125596718/article/details/6901935

我们需要得到Size[x]
那么我们就先需要得到Size[y] (x与y是有连接的)。
那么Size[x] = Size[y1] U Size[y2] ……
这个并集,是对Size[y1]能到达的结点 U Size[y2]能到达的结点。
也就是说如果有一个结点y1 和y2都能到达,那么对于x来说就只要求一次,所以是求并集。而并不是盲目的把y1能到达的结点个数+y2能到达的结点个数。
对于求并集来说,我们可以用二进制来表示。这就需要用到bitset了。
同时,我们求x,是需要求出y的。那么可以想一下,拓扑序序列一定是x在前y在后,所以我们可以用拓扑序,从后往前求。

#include"stdio.h"
#include"string.h"
#include"vector"
#include"map"
#include"queue"
#include"iostream"
#include"algorithm"
#include <bitset>
using std::bitset;
using namespace std;
typedef pair<int,int> PII;

int N,M;
vector<int> Q[30010];
int vis[30010],Size[30010],in[30010];
int num[30010],to[30010],top;
map<PII,int> P;
bitset<30010>f[30010];

void topo()
{
    queue<int> Qu;
    for(int i = 1; i <= N; i ++)
        if(in[i] == 0)
           Qu.push(i);
    while(!Qu.empty())
    {
        int v = Qu.front();
        to[++ top] = v;
        Qu.pop();
        for(int i = 0; i < Q[v].size(); i ++)
        {
            int u = Q[v][i];
            in[u] --;
            if(in[u] == 0)
                 Qu.push(u);
        }
    }
}

int main()
{
    scanf("%d%d",&N,&M);
    for(int i = 1; i <= M; i ++)
    {
        int x,y;scanf("%d%d",&x,&y);
        if(P[{x,y}] == 0)
         {
             Q[x].push_back(y);
             in[y] ++;
         }
         P[{x,y}] = 1;
    }
    topo();
    for(int i = top; i >= 1; i --)
    {
        int u = to[i];
        f[u].set(u);
        for(int j = 0; j < Q[u].size(); j ++)
            f[u] |= f[Q[u][j]];
        Size[u] = f[u].count();
    }
    for(int i = 1; i <= N; i ++)
        printf("%d\n",Size[i]);
}


其实也可以不用拓扑排序,直接dfs。每次都更新一下f值就可以了

#include"stdio.h"
#include"string.h"
#include"vector"
#include"map"
#include"queue"
#include"iostream"
#include"algorithm"
#include <bitset>
using std::bitset;
using namespace std;
typedef pair<int,int> PII;

int N,M;
vector<int> Q[30010];
int vis[30010],Size[30010],in[30010];
int num[30010],to[30010],top;
map<PII,int> P;
bitset<30010>f[30010];

void dfs(int x)
{
    f[x].set(x);
    for(int i = 0; i < Q[x].size(); i ++)
    {
        int v = Q[x][i];
        if(vis[v] == 0)
        {
            vis[v] = 1;
            dfs(v);
        }
        f[x] |= f[v];
    }
}

int main()
{
    scanf("%d%d",&N,&M);
    for(int i = 1; i <= M; i ++)
    {
        int x,y;scanf("%d%d",&x,&y);
        if(P[{x,y}] == 0)
         {
             Q[x].push_back(y);
             in[y] ++;
         }
         P[{x,y}] = 1;
    }
    for(int i = 1; i <= N; i ++)
    {
        if(vis[i] == 0)
        {
           vis[i] = 1;
           dfs(i);
        }
    }

    for(int i = 1; i <= N; i ++)
        printf("%d\n",f[i].count());
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值