有向无环图 $=>$ 拓扑排序 $=>$ 拓扑图 $=>$每一个状态都没有循环依赖$=>$没有后效性$=>$可以DP / 递推求解答案(比如最短 / 长路)
我们用$f[i]$表示i可以到达多少个点.
例如:$i$ 可以到达的点为 $j_1,j_2$ ,则$f[i] =f[i] ∪ f[j_1]∪f[j_2]$
算 $f[i]$ 的时候把所有的$f[i]$都算出来$=>$用拓扑排序的逆序推一遍即可
为了防止计算重复,我们可以使用状态压缩,用一个长度为n的二进制字符串代表它的状态。
但是数据较大,会超时
我们可以用STL中的$bitset$存每一个点可以到达的点用f[i].count()
计算有多少个1也就是有多少个可到达的点。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<bitset>
using namespace std;
const int N = 50007, M = 500007;
int n, m;
int ver[M], nex[M], head[M], tot;
int din[N];
int q[N];
bitset<N>f[N];//它本身就是一个容器,这里开N个bitset容器
void add(int x,int y){
ver[tot] = y;
nex[tot] = head[x];
head[x] = tot ++ ;
din[y] ++ ;
}
void toposort(){
int hh = 0, tt = -1;
for(int i = 1;i <= n;++i){
if(!din[i]){
q[++ tt ] = i;
if(tt == N)tt = 0;
}
}
while(hh <= tt){
int x = q[hh ++ ];
if(hh == N)hh = 0;
for(int i = head[x];~i;i = nex[i]){
int y = ver[i];
if(-- din[y] == 0){
q[++ tt] = y;
if(tt == N)tt = 0;
}
}
}
}
void calc(){
for(int i = n - 1;i >= 0;-- i){
int x = q[i];
f[x][x] = 1;//the initialization of DP,表示自己能到达自己
for(int j = head[x];~j;j = nex[j]){
int y = ver[j];
f[x] |= f[y];//求并集
}
}
}
int main(){
scanf("%d%d", &n, &m);
memset(head,-1,sizeof head);
for(int i = 1;i <= m;++i){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
toposort();
calc();
for(int i = 1;i <= n;++i)
printf("%d\n",f[i].count());//一共就多少个1就代表能到达多少个点
return 0;
}
妙