【问题描述】
YOUSIKI 在 noip2016 的一道《天天爱跑步》的题爆零后,潜心研究树上问题,成为了一代大师,于是皮皮妖为了测验他,出了一道题,名曰《跑步爱天天》。
有一个以1为根的有根树,初始每个点都有个警卫,每个警卫会按深度优先的顺序周期性的巡逻以其初始点为根的子树(详见样例解释),一个时刻走且仅走一条边。
YOUSIKI 初始在x点,他要到根结点拜访皮皮妖,他会沿着最短路径走,一个时刻走且仅走一条边,当他走到这个点时,如果遇到了警卫,他会消耗1点妖气将这个警卫杀死,杀死后的警卫就不会在以后的路程中出现。
那么 YOUSIKI 需要消耗几点妖气才能拜访到皮皮妖呢?
【输入格式】
第一行一个数字 T,表示有 T 组数据。
对于每组数据,第一行一个整数 n,表示树有 n 个结点。
接下来 n 行,第 i 行有一个整数 k,表示 i 号点儿子个数,接下来 k 个整数,表示 k 个有序儿子 (“有序” 的含义详见样例解释)。
最后一行一个整数 x,表示 YOUSIKI 的出发点。
【输出格式】
输出 T行,每行一个整数表示答案。
【样例输入1】
1
6
2 2 3
2 4 5
1 6
0
0
0
6
【样例输出1】
1
【样例解释】
为了方便,我们把初始在i号点的警卫称为警卫i。
警卫1的一个周期内的巡逻路线为:1->2->4->2->5->2->1->3->6->3->1。
警卫2的一个周期内的巡逻路线为:2->4->2->5->2。
警卫3的一个周期内的巡逻路线为:3->6->3。
警卫4,5,6一直不动。
YOUSIKI 的路线为:6->3->1。
YOUSIKI 初始在6号点,需要杀掉警卫6。第一时刻他在3号点,虽然他和警卫3对穿过去,但是由于没有在点上相遇,所以不算相遇。第二时刻他在1号点,此时1号点没有警卫。
注意
警卫的巡逻是周期性的,例如,初始在2号点警卫的巡逻路线为:2->4->2->5->2->4->2->5->2->4->2->5->2->...
输入格式中的 “有序” 指的是比如1号点的儿子先输入的2再输入的3,那么1号点巡逻时就要先巡逻2再巡逻3。
本题输入文件较大,可以使用快速输入输出
【数据范围与约定】
对于20%的数据,n≤100。
对于40%的数据:n≤2000。
对于另外10%的数据:树高≤5。
对于另外10%的数据:树是一条链。
对于100% 的数据:T≤10,n≤500000。
#include
using namespace std;
const int MAXN=500005;
int N,S,np,tot,Clock,last[MAXN],dep[MAXN],dfn[MAXN*10];
int mark[MAXN],V[MAXN],Time[MAXN],fa_first[MAXN*10],vis[MAXN];
struct edge{int to,pre;}E[MAXN];
char c;
void scan(int &x)
{
for(c=getchar();c<'0'||c>'9';c=getchar());
for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}
void addedge(int u,int v)
{
E[++np]=(edge){v,last[u]};
last[u]=np;
}
void init()
{
memset(last,0,sizeof(last));
memset(mark,0,sizeof(mark));
memset(vis,0,sizeof(vis));
memset(vis,0,sizeof(vis));
np=0; tot=0; Clock=0;
}
void dfs(int i)
{
dfn[++tot]=i; fa_first[tot]=1; //加入序列,第一次出现打标记
if(S==i) mark[i]=1; //把S的路径标记
for(int p=last[i];p;p=E[p].pre)
{
int j=E[p].to;
dep[j]=dep[i]+1;
dfs(j);
if(mark[j]) mark[i]=1; //S所在路径
dfn[++tot]=i; fa_first[tot]=0; //i不是第一次出现,标记打为0
}
if(mark[i]) Time[i]=Clock++; //如果在路径上,记录所用时间
}
int main()
{
int T,i,u,v;
scan(T);
while(T--)
{
init();
scan(N);
for(u=1;u<=N;u++)
{
scan(i);
for(int k=1;k<=i;k++) scan(V[k]); //有序
for(int k=i;k>=1;k--) addedge(u,V[k]);
}
scan(S);
dep[1]=1;
dfs(1);
int ans=0;
for(i=1;i<=tot;i++)
{
u=dfn[i];
if(mark[u]&&i-Time[u]>0&&fa_first[i-Time[u]])
{ //u在S的路径上,且距u为Time的节点是第一次出现。中间的判断是防止负数
v=dfn[i-Time[u]];
if(mark[v]&&dep[v]<=dep[u]&&!vis[v])
{ //v在S的路径上 且是u的父祖 且没有计算过
ans++;
vis[v]=1;
}
}
}
cout<