P2835 刻录光盘
好久没写图论练练手。
这道题有两种解法:tarjan和并查集。我用的是tarjan缩点。
缩完点之后就只需要找出入度为0的新点有多少个就可以了。
md这道题卡快读???
代码:
#include<cstdio>
#include<algorithm>
const int maxn = 205;
struct Nodes
{
int next, to;
} e[400005], e2[400005];
int head[maxn], tot;
int head2[maxn], tot2;
int dfn[maxn], low[maxn], dtot;
int stack[maxn], top;
int color[maxn], ctot;
bool vis[maxn];
bool b[maxn][maxn];
int indegree[maxn];
int n;
void link(int u, int v)
{
e[++tot] = (Nodes){head[u], v};
head[u] = tot;
}
void link2(int u, int v)
{
e2[++tot2] = (Nodes){head2[u], v};
head2[u] = tot2;
}
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
return s * ans;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++dtot;
stack[++top] = u; vis[u] = true;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(!dfn[v])
{
tarjan(v);
low[u] = std::min(low[u], low[v]);
}
else if(vis[v]) low[u] = std::min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
ctot++;
while(stack[top + 1] != u)
{
int x = stack[top--]; vis[x] = false;
color[x] = ctot;
}
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
int temp; scanf("%d", &temp);
while(temp != 0)
{
link(i, temp);
scanf("%d", &temp);
}
}
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
for(int u = 1; u <= n; u++)
{
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(color[u] != color[v] && !b[color[u]][color[v]])
{
link2(color[u], color[v]);
indegree[color[v]]++;
b[color[u]][color[v]] = true;
}
}
}
int ans = 0;
for(int i = 1; i <= ctot; i++)
{
if(indegree[i] == 0) ans++;
}
printf("%d\n", ans);
return 0;
}