跑步爱天天

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值