题解:P10480 可达性统计
P10480 可达性统计 题解
题目简述:
给定一个
思路简述:
由于是有向无环图,我们不难想到用拓扑排序进行求解。当然你不用拓扑排序,暴力也是可以的,下文会提到。然而拓扑排序预处理完后,我们需要怎么统计呢?
这时候,我们就需要用到一个特殊的容器——bitset!!
bitset 简述:
bitset 可以看作是一个数组,但是它仅存真伪值(即 bool)。
因为 bitset 过于冷门,下面是它的用法介绍(更详细的在这里):
定义一个 bitset 型数组 f[MAXN]:
bitset<MAXN> f[MAXN];//对,和vector或queue差不多
查看某个 bitset 变量的 true 的数量:
f[i].count();
至于其他的运算情况,跟普通的变量差不多,不予阐述。
好啦,上述成员函数已经够您做完这道题了,继续看思路吧。
具体实施方案:
将所有点的入度统计下来,依次从入度的小到大遍历每个点,计算入度,然后遍历每个点。因为能够遍历的点的数量一定是递增的,所以我们赋值可以用到或运算,即前面访问过的,将其延续下来,需要用到状态压缩的基础知识。
核心代码:
void dfs(int u)//以u为出发点
{
if(book[u]) return;//已经访问过啦
book[u]=true;
for(int i=head[u];i;i=edge[i].pre)//链式前向星遍历
{
dfs(edge[i].to);
f[u]|=f[edge[i].to];//看清楚,是|=
}
return;
}
具体代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int MAXN=30005;
bitset<MAXN> f[MAXN];
struct EDGE{//结构体存图
int from,to,pre;
}edge[MAXN];
bool book[MAXN];
int u,v,cnt,head[MAXN],t;
queue<int> q;//用于拓扑排序
int in[MAXN];
void add(int from,int to)//加边
{
edge[++cnt].from=from;
edge[cnt].to=to;
edge[cnt].pre=head[from];
head[from]=cnt;
return;
}
void dfs(int u)//核心代码,上述
{
if(book[u]) return;
book[u]=true;
for(int i=head[u];i;i=edge[i].pre)
{
dfs(edge[i].to);
f[u]|=f[edge[i].to];
}
return;
}
vector<int > num;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) f[i][i]=true;//初始化不能漏!
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
in[v]++;//入度+1
add(u,v);
}
for(int i=1;i<=n;i++)//拓扑排序基础流程
if(!in[i])
q.push(i),num.push_back(i);
while(!q.empty())
{
t=q.front();
q.pop();
for(int i=head[t];i;i=edge[i].pre)//遍历每条连边
{
if(--in[edge[i].to]<=0)//加入队列
{
q.push(edge[i].to);
num.push_back(edge[i].to);
}
}
}
for(auto i:num)//遍历num
dfs(i);
for(int i=1;i<=n;i++)
printf("%d\n",f[i].count());
return 0;
}
当然,你不用拓扑排序,直接朴素算法,也是没问题的,如下:
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int MAXN=30005;
bitset<MAXN> f[MAXN];
struct EDGE{
int from,to,pre;
}edge[MAXN];
bool book[MAXN];
int u,v,cnt,head[MAXN];
void add(int from,int to)
{
edge[++cnt].from=from;
edge[cnt].to=to;
edge[cnt].pre=head[from];
head[from]=cnt;
return;
}
void dfs(int u)
{
if(book[u]) return;
book[u]=true;
for(int i=head[u];i;i=edge[i].pre)
{
dfs(edge[i].to);
f[u]|=f[edge[i].to];
}
return;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) f[i][i]=true;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
}
for(int i=1;i<=n;i++)
{
dfs(i);
printf("%d\n",f[i].count());
}
return 0;
}
对,我太懒了,就是改了改,没重写。
拓扑排序方法 AC 记录
朴素算法 AC 记录