https://nanti.jisuanke.com/t/17354
对于 50% 的数据:直接模拟
对于另外10%的数据:因为地图是一条链,显然 YOUSIKI 会消灭所有距离他为偶数条边的祖
先。
对于100%的数据:我们先把整个树 dfs 一遍,遇到一个点就把这个点记录到一个数组后边,
即求出了树的欧拉序,显然如果不考虑循环的话,guard是在这个序列上每次往后走一个,起
始位置就是第i个点第一次出现的位置。
而显然 YOUSIKI 只可能与他的所有祖先相遇,所以我们把他所有祖先的起始位置标记出来。
假设 YOUSIKI 现在走到了 x 点,过了 t 秒,那么我们在这个序列上遍历 x 出现的所有位置,
并查看这个位置往前 t 个是否为 x 的祖先,如果是,把那个祖先标为1,表示已被消灭。
复杂度 。
#include <cstdio>
#include <cstring>
#define fin stdin
#define fout stdout
//FILE *fin = fopen("in.txt", "r");
//FILE *fout = fopen("out1.txt", "w");
inline int nextChar() {
static const int siz = 1 << 25;
static char buf[siz];
static char *head = buf + siz;
static char *tail = buf + siz;
if (head == tail)
fread(head = buf, 1, siz, fin);
return int(*head++);
}
inline int nextInt() {
int r = 0, c = nextChar();
for (; c < 48; c = nextChar());
for (; c > 47; c = nextChar())
r = r * 10 + c - 48;
return r;
}
const int mxn = 5000015;
const int mxm = 20000015;
int n, s, tt;
int hd[mxn];
int nt[mxn];
int to[mxn];
inline void addEdge(int u, int v) {
nt[++tt] = hd[u], to[tt] = v, hd[u] = tt;
}
int dfn[mxm];
int dep[mxn];
int mrk[mxn];
int tim[mxn];
int vis[mxn];
int fst[mxn];
int tmp;
int tot;
int ans;
bool dfs(int u) {
dfn[++tot] = u, fst[tot] = 1;
mrk[u] = (u == s);
for (int i = hd[u], v; i; i = nt[i]) {
v = to[i];
dep[v] = dep[u] + 1;
mrk[u] = mrk[u] | dfs(v);
dfn[++tot] = u, fst[tot] = 0;
}
if (mrk[u])tim[u] = tmp++;
return mrk[u];
}
signed main() {
for (int cas = nextInt(); cas--; ) {
n = nextInt();
memset(hd, 0, sizeof(int) * (n + 5)), tt = 0;
for (int i = 1; i <= n; ++i) {
static int stk[mxn];
int son = nextInt();
for (int j = 1; j <= son; ++j)
stk[j] = nextInt();
for (int j = son; j >= 1; --j)
addEdge(i, stk[j]);
}
s = nextInt();
tmp = tot = 0, dfs(1);
memset(vis, 0, sizeof(int) * (n + 5)), ans = 0;
for (int i = 1, u, v; i <= tot; ++i)
if (mrk[u = dfn[i]] && i > tim[u] && fst[i - tim[u]])
if (mrk[v = dfn[i - tim[u]]] && dep[v] <= dep[u])
ans += (vis[v] == 0), vis[v] = 1;
fprintf(fout, "%d\n", ans);
}
fclose(fin);
fclose(fout);
return 0;
}