这道题初看时没想到过树,看了题解才知道可以建虚树。
对题目分析可以将b数组想成一颗树,第i个b数组就是点i到根节点的上路径的所有点的集合。
那么在构造树时就相当于将a数组中的所有点取lca ,再将i节点连到lca上,作为lca的儿子。
这样的复杂度是O(nlogn)的。
在询问时离线处理,dfs更新每个点对问题的贡献。
其深度减去lca的深度即为贡献。
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N=200005;
struct node {
int len;
vector<int> to;
}a[N],que[N];
int n,m,lca;
int fa[N][20];
int dep[N];
vector <int> son[N];
int sig[N];
int ans[N];
inline int getlca(int x,int y)
{
int p,st=0;
if (dep[x]<dep[y]) p=x,x=y,y=p;
p=dep[x]-dep[y];
while (p)
{
if (p&1) x=fa[x][st];
p/=2,st++;
}
st=0;
while (x!=y)
{
if (fa[x][st]!=fa[y][st]||(fa[x][st]==fa[y][st])&&!st)
{
x=fa[x][st];
y=fa[y][st];
st++;
}
else st--;
}
return x;
}
void dfs(int x)
{
int i;
for (i=0;i<que[x].to.size();i++)
{
if (sig[que[x].to[i]]==-1)
{
ans[que[x].to[i]]+=dep[x];
sig[que[x].to[i]]=x;
}
else
{
ans[que[x].to[i]]+=dep[x]-dep[getlca(x,sig[que[x].to[i]])];
sig[que[x].to[i]]=x;
}
}
for (i=0;i<son[x].size();i++) dfs(son[x][i]);
}
int main()
{
register int i,j;
scanf("%d",&n);
int x,st;
for (i=1;i<=n;i++)
{
scanf("%d",&a[i].len);
for (j=1;j<=a[i].len;j++)
scanf("%d",&x),a[i].to.push_back(x);
}
dep[0]=1;
for (i=1;i<=n;i++)
{
if (!a[i].len)
{
fa[i][0]=0;
dep[i]=dep[0]+1;
son[0].push_back(i);
continue;
}
lca=a[i].to[0];
for (j=1;j<a[i].len;j++) lca=getlca(lca,a[i].to[j]);
son[lca].push_back(i);dep[i]=dep[lca]+1;
st=0;fa[i][st]=lca;
while (fa[i][st]) fa[i][st+1]=fa[fa[i][st]][st],st++;
}
scanf("%d",&m);
for (i=1;i<=m;i++)
{
scanf("%d",&que[i].len);
for (j=1;j<=que[i].len;j++)
scanf("%d",&x),que[x].to.push_back(i);
}
memset(sig,-1,sizeof(sig));
dfs(0);
for (i=1;i<=m;i++)
printf("%d\n",ans[i]-1);
return 0;
}