ch 2101 可达性统计 拓扑排序+状压dp

题目:

描述

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

输入格式

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

输出格式

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

样例输入

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

解:

  1.首先想到的是从后向前开始更新每一个点的可达值-->拓扑排序提供从后向前更新的可能

  2.继而思考如何从后向前更新,设拓扑序(x,y),x在y前,那么,若x可达y,那么y的所有可达点都是x的可达点

  3.我们不妨使用状态压缩方法,对一串二进制数ans[x],1表示第i位对第x位来说可达,0表示不可达

  4.在3的基础上知道,可以使用 ans[x]=ans[x]|ans[y] 向前更新可达值

  5.为了实现方便,引入 birset<size> ans[maxn] 来存储状压数据

AC code:

#include<bits/stdc++.h>
using namespace std;
bitset<30005> ans[30005];
const int maxn=30000+5;
int deg[maxn];
int topo[maxn];
vector<int> dag[maxn];
vector<int>::iterator it;
int tot,cnt,n,m;
void toposort()
{
    queue<int> que;
    for(int i=1;i<=n;i++)
    {
        if(deg[i]==0)    que.push(i);
    }
    while(que.size())
    {
        int x=que.front();
        que.pop();
        topo[++cnt]=x;
        for(int i=0;i<dag[x].size();i++)
        {
            int y=dag[x][i];
            if(--deg[y]==0)    que.push(y);
        }
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        dag[u].push_back(v);
        deg[v]++;
    }
    toposort();
    for(int i=cnt;i>0;--i)
    {
        int x=topo[i];
        ans[x].reset();
        ans[x][x]=1;
        for(int i=0;i<dag[x].size();i++)
        {
            ans[x]|=ans[dag[x][i]];
        }
    }
    for(int i=1;i<=n;i++)
        cout<<ans[i].count()<<endl;
    return 0;
}

 

posted on 2020-04-28 10:44  Caution_X  阅读(188)  评论(0编辑  收藏  举报

导航