分析:
如果整个图是一棵树,那么针对每个询问
必经路径就是k个点的lca到根节点的路径
实际上扩展成一个普通的图也是这样
听男生们在讨论的时候,说这道题和灾难那道题”一样“
我仔细一想,确实哎,
我们现在手上的图是一个DAG
首先我们把ta的拓扑序计算出来
之后构造一个“拓扑树”:
假设当前点是x,x的祖先一定都在拓扑树中了
我们要找到与原图中直接指向x的所有点在拓扑树中的lca,把x挂在lca下
(根节点到x的路径一定会经过lca)
处理询问的时候,我们只要在拓扑树上寻找这k个点的lca,输出lca到根节点的结点个数即可
//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
const int INF=0x33333333;
const int N=50005;
struct node{
int x,y,nxt;
};
node way[N<<1],e[N<<1];
int st[N],tot=0,n,m,q,ste[N],tet=0;
int deep[N],pre[N][20],lg,f[N],cnt[N],Q[N],in[N],tou,wei;
bool vis[N];
void add(int u,int w) //用来top的
{
tet++;
e[tet].x=u;e[tet].y=w;e[tet].nxt=ste[u];ste[u]=tet;
}
void ad(int u,int w) //用来建树的
{
tot++;
way[tot].x=u;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
}
int LCA(int u,int w)
{
if (deep[u]<deep[w]) swap(u,w);
int d=deep[u]-deep[w];
if (d)
for (int i=0;i<=lg&&d;i++,d>>=1)
if (d&1)
u=pre[u][i];
if (u==w) return u;
for (int i=lg;i>=0;i--)
if (pre[u][i]!=pre[w][i])
{
u=pre[u][i];
w=pre[w][i];
}
return pre[u][0];
}
void Top()
{
tou=wei=0;
for (int i=1;i<=n;i++) if (in[i]==0) Q[++wei]=i,in[i]=INF;;
while (tou<wei)
{
int now=Q[++tou],lca;
for (int i=ste[now];i;i=e[i].nxt)
{
in[e[i].y]--;
if (in[e[i].y]==0) {
Q[++wei]=e[i].y;
in[e[i].y]=INF;
}
}
}
}
void doit()
{
memset(pre,0,sizeof(pre));
deep[1]=1;
for (int j=2;j<=wei;j++)
{
int now=Q[j];
int lca=-1;
for (int i=st[now];i;i=way[i].nxt) //原图中前驱的lca
if (lca==-1) lca=way[i].y;
else lca=LCA(lca,way[i].y);
deep[now]=deep[lca]+1;
pre[now][0]=lca;
for (int i=1;i<=lg;i++) //一边添加,一边维护pre
pre[now][i]=pre[pre[now][i-1]][i-1];
}
}
int main()
{
//freopen("attack.in","r",stdin);
//freopen("attack.out","w",stdout);
memset(in,0,sizeof(in));
scanf("%d%d%d",&n,&m,&q);
lg=log(n)/log(2)+1;
for (int i=1;i<=m;i++)
{
int u,w;
scanf("%d%d",&u,&w);
add(u,w); ad(w,u);
in[w]++;
}
Top();
doit();
for (int i=1;i<=q;i++)
{
int x,u,now;
scanf("%d",&x);
scanf("%d",&now);
for (int j=2;j<=x;j++)
{
scanf("%d",&u);
now=LCA(u,now);
}
printf("%d\n",deep[now]);
}
return 0;
}