P2002 消息扩散
题目概述
给定一张有向图,不保证无自环与重边,信息从某几个节点出发,沿单向路传播,现在给出n个节点及其之间的道路,问至少需要在几个节点发布信息才能让这所有节点都得到信息。
数据规模:n≤100000,m≤500000
时空限制:1s,256M
思路:
利用缩点的思想,先预处理一下所有的强连通分量,然后把每个强连通分量内的所有节点看做一个节点,然后处理一张新图,O(M)的时间检查每个点的入度,然后取入度为0的点的个数,即为信息出发点,且上述命题互为充要条件。
证明:
1.充分性证明:如果入度为0的点不是信息出发点,那么这个点必定不会接收到任何节点发出的信息,因为它的入度为0。
2.必要性证明:如果信息出发点不是入度为0的点,那么其必有入度点,使其覆盖点更多,效率更高。
#include<iostream>
using namespace std;
int i,j,n,m;
bool b[100001];
int color[100001],colorn;
int stack[100001],top;//数组模拟栈
int s,e,temp,index,ans,bb[100001];
int dfn[100001],low[100001];
int head[100001],rd[100001];
struct data
{
int v,u;
data *nxt;
}a[1000001];
int r()
{
char ch=getchar();
int ans=0;
while(ch<'0'||ch>'9')
{
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ans*=10;
ans+=ch-'0';
ch=getchar();
}
return ans;
}
int ins(int ss,int ee)
{
temp++;
a[temp].nxt=&a[head[ss]];
a[temp].u=ss;
head[ss]=temp;
a[temp].v=ee;
}
void tarjan(int x)
{
dfn[x]=++index;
low[x]=index;
b[x]=1,bb[x]=1;
stack[++top]=x;
data *p=&a[head[x]];
while(p->v!=0)
{
int vv=p->v;
if(!dfn[vv])
{
tarjan(vv);
low[x]=min(low[x],low[vv]);
}
else if(b[vv])
{
low[x]=min(low[x],dfn[vv]);
}
p=p->nxt;
}
if(dfn[x]==low[x])
{
b[x]=0;
color[x]=++colorn;
while(stack[top]!=x)
{
color[stack[top]]=colorn;
b[stack[top--]]=0;
}
top--;
}
}
int main()
{
n=r(),m=r();
for(i=1;i<=m;i++)
{
s=r(),e=r();
ins(s,e);
}
for(i=1;i<=n;i++)
if(!bb[i])
tarjan(i);
for(i=1;i<=m;i++)
{
if(color[a[i].v]!=color[a[i].u])
rd[color[a[i].v]]++;
}
for(i=1;i<=colorn;i++)
if(rd[i]==0)
ans++;
cout<<ans;
}