笔者没 ac ,因为没绑定没提交。但在其他 OJ 上提交 ac 了,可以确保代码正确性。
一道强连通分量的板子题。
思路:
最终可以满足条件的女孩 既要王子喜欢又可以被娶(也就是巫师那个名单) ,这不就类似强连通分量吗?只要王子喜欢的女孩和王子在同一个强连通分量里就都满足条件。
这道题和板子的区别主要在于建边和答案输出,主要说说这俩。
1. 建边:
-
在王子到喜欢的女孩之间建边;
-
在可以被娶女孩和王子之间建边。
这些结合题目及思路,不难理解。
2. 输出
概述:从王子喜欢的女孩中挑和王子在同一个强连通分量的女孩,保存起来,在按编号升序输出。
-
用前向星或 vector 找到和王子有直接连接的女孩(王子所喜欢的女孩);
-
判断两者是否在同一强连通分量中;
-
用变量记录女孩数量,用数组存下来女孩编号;
-
搜索完毕后,排序输出即可。
细节:
-
女孩的编号为保证不和王子重复,使第 i 个女孩的编号为 i + n ;
-
如需要要写上快读快写,某 OJ 用 cin 和 cout 也能过;
-
数据范围用 2000005
,是越大越好。
代码:
(这里把快读快写也附上了。)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2000005;
int n;
int cnt, hd[maxn];
struct node{
int to, nxt;
}e[maxn * 2];
int tmp, dfn[maxn], low[maxn];
int st[maxn], top;
int co[maxn], col;
int ans[maxn];
int read ()
{
int x = 1, s = 0;
char ch = getchar ();
while (ch < '0' or ch > '9') {if (ch == '-') x = -1; ch = getchar ();}
while (ch >= '0' and ch <= '9'){s = s * 10 + ch - '0'; ch = getchar ();}
return x * s;
}
void write (int x)
{
if (x == 0) return;
write (x / 10);
putchar (x % 10 + '0');
}
void add (int u, int v)
{
e[++cnt].to = v;
e[cnt].nxt = hd[u];
hd[u] = cnt;
}
void tarjan (int u)
{
dfn[u] = low[u] = ++tmp;
st[++top] = u;
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan (v);
low[u] = min (low[u], low[v]);
}
else if (!co[v]) low[u] = min (low[u], dfn[v]);
}
if (dfn[u] == low[u])
{
col++;
co[u] = col;
while (st[top] != u)
{
co[st[top]] = col;
top--;
}
top--;
}
}
int main ()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int k;
cin >> k;
for (int j = 1; j <= k; j++)
{
int v;
cin >> v;
add (i, v + n);
}
}
for (int i = 1; i <= n; i++)
{
int v;
cin >> v;
add (v + n, i);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i]) tarjan (i);
}
for (int i = 1; i <= n; i++)
{
int tot = 0;
for (int j = hd[i]; j; j = e[j].nxt)
{
int v = e[j].to;
if (co[i] == co[v])
{
tot++;
ans[tot] = v - n;
}
}
cout << tot << " ";
sort (ans + 1, ans + tot + 1);
for (int l = 1; l <= tot; l++)
{
cout << ans[l] << " ";
}
cout << endl;
}
return 0;
}