T a r j a n Tarjan Tarjan模板题
思路分析:
先用
t
a
r
j
a
n
tarjan
tarjan缩点,保证图中没有环。
由于不存在环,对于每条链,只需要将软件发给入度为 0 0 0的点,就可以传给这条链上所有的点。因此,任务 A A A就转化为了求缩点后的图中有多少个点入度为 0 0 0。
对于任务
B
B
B,“ 不论我们给哪个学校发送新软件,它都会到达其余所有的学校 ”,则图中一定不存在入度为
0
0
0和出度为
0
0
0的点。
只要在入度为0的点和出度为
0
0
0的点之间连一条边,就可以同时消灭两个“不合法”的点。再给每个多出来的入度(出度)为
0
0
0的点随便找一个出度(入度)为
0
0
0的点配对。因此,入度为
0
0
0的点数与出度为
0
0
0的点数的较大值即为任务B的答案。
代码:
#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>
using namespace std;
int read()
{
int x = 0;
int f = 1;
char ch = getchar();
for(;!isdigit(ch);ch = getchar())
if(ch == '-')
f = -1;
for(;isdigit(ch);ch = getchar())
x = x * 10 + (ch ^ 48);
return x * f;
}
void print(int x)
{
if(x < 0)
{
putchar('-');
x = -x;
}
char ch = x % 10 + '0';
if(x > 9)
print(x / 10);
putchar(ch);
}
const int N = 10010,M = 50010;
struct edge
{
int to,nxt;
}e[M];
int head[N],cnt;
void add(int u,int v)
{
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int n,m;
int dfn[N],low[N],dfs_clock,vis[N],belong[N];
int num[N],color_num;
int ans;
stack<int> s;
void tarjan(int x)
{
dfn[x] = low[x] = ++dfs_clock;
s.push(x);
vis[x] = true;
for(int i = head[x];i;i = e[i].nxt)
{
int v = e[i].to;
if(!dfn[v])
{
tarjan(v);
low[x] = min(low[x],low[v]);
}
else
if(vis[v])
low[x] = min(low[x],dfn[v]);
}
if(dfn[x] == low[x])
{
belong[x] = ++color_num;
while(s.top() != x)
{
int t = s.top();
s.pop();
belong[t] = color_num;
num[color_num]++;
vis[t] = false;
}
s.pop();
num[color_num]++;
vis[x] = false;
}
}
int ans1,ans2;
int in[N],out[N];
int main()
{
int u;
n = read();
for(int i = 1;i <= n;i++)
while(1)
{
u = read();
if(!u)
break;
add(i,u);
}
for(int i = 1;i <= n;i++)
if(!dfn[i])
tarjan(i);
for(int i = 1;i <= n;i++)
for(int j = head[i];j;j = e[j].nxt)
{
int v = e[j].to;
if(belong[i] ^ belong[v])
out[belong[i]]++,in[belong[v]]++;
}
for(int i = 1;i <= color_num;i++)
{
if(!in[i])
ans1++;
if(!out[i])
ans2++;
}
ans2 = max(ans1,ans2);
if(color_num == 1)
ans2 = 0;
print(ans1),putchar('\n'),print(ans2);
}