PS:如果读过题了可以跳过题目描述直接到题解部分
提交链接:洛谷 T281315 掌控
题目
题目描述
公元 2044 年,人类进入了宇宙纪元。L 国有
n
n
n 个星球,分别编号为
1
1
1 到
n
n
n ,每一星球上有一个球长。有些球长十分强大,可以管理或掌控其他星球的球长,具体来说,第
i
i
i 个星球的球长管理
k
i
+
1
k_i+1
ki+1 个星球的球长,分别是
a
i
1
,
a
i
2
,
.
.
.
,
a
i
k
i
(
a
i
j
<
i
)
a_{i1},a_{i2},...,a_{ik_i} (a_{ij}<i)
ai1,ai2,...,aiki(aij<i) ,但若想要掌控一个星球的球长,就没那么容易了,第
i
i
i 个星球的球长掌控第
j
j
j 个星球的球长当且仅当他管理的所有球长都掌控第
j
j
j 个星球的球长,当然,所有球长都掌控他自己。
这些球长要召开
q
q
q 次会议,每次会议由
t
i
t_i
ti 个球长召开,所有被他们掌控的球长都会参加,你作为宇宙会议室室长,需要知道每次会议有多少个球长参加。
输入格式
第一行一个数
n
n
n ,表示星球的个数;
接下来
n
n
n 行,每一行首先给出一个
k
i
k_i
ki (可能为
0
0
0 ),接下来
k
i
k_i
ki 个数,描述第 i 个星球的球长管理的球长。保证没有重复;
接下来一行,给出一个数
q
q
q ,表示询问的个数;
接下来
q
q
q 行,每一行描述一个询问:格式同上文的格式。不保证没有重复(重复的球长当做只出现了一次)
输出格式
输出共 q q q 行,第 i i i 行输出第 i i i 次询问的答案。
样例
输入
7
0
1 1
1 1
1 2
2 2 3
0
2 2 6
3
2 2 3
2 3 5
2 4 5
输出
3
3
4
提示
对于第一个询问,2、3号球长都掌控1号球长,所以总共有3个球长参会,编号分别为1,2,3;
对于第二个询问,3、5号球长都掌控1号球长,所以总共有3个球长参会,编号分别为1,3,5;
对于第三个询问,4号球长掌控第1、2号球长,所以总共有4个球长参会,编号分别为1,2,4,5;
特别说明:第5号球长没有掌控球长2,因为
3
∈
k
5
3∈k_5
3∈k5,但
2
2
2 不属于
k
3
k_3
k3 。但球长4掌控球长2,因为球长掌控自己。
图片说明:
u
−
>
v
u->v
u−>v 表示
v
v
v 管理
u
u
u
题解
首先,我们先用数学归纳法来证明一下他们之间的掌控关系是一棵树:
- 当只有一个节点时,它是一棵树。
- 假设他们之间的掌控关系已经是一棵树时,我们向里面加入一个节点,那么它可以掌控的节点就是所有它管理的节点的最近公共祖先到根节点之间的所有节点,我们就把这个节点加在这个最近公共祖先下面,那他们还是一棵树。
- 综上,他们之间的掌控关系是一棵树。
所以,要求他们的掌控的球长,就建一棵树就好了。
比如,样例建出来的树就长这样:
然后,我们从根节点“0”深搜遍历并打上dfn。
样例的树打上dfn以后是这样的:
再将所有召开的球长序号按dfn序排序,总的参会个数就是每次加上当前球长到当前球长和排序后的前一个球长的最近公共祖先之间的球长个数(即dep差)
样例的询问三的查询大概添加过程是这样的:
代码实现
//洛谷 T281315 掌控
#pragma GCC optimize(3)
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int n,q;//200000
int k,t;//2000000
int x;
vector<int> v[200010];
int fa[200010][31];
int dep[200010];
int dfn[200010];
int cnt;
int a[2000010];
int ans;
void in(int &x){
int nt;
x=0;
while(!isdigit(nt=getchar()));
x=nt^'0';
while(isdigit(nt=getchar())){
x=(x<<3)+(x<<1)+(nt^'0');
}
}
void dfs(int x){
dfn[x]=++cnt;
int sz=v[x].size();
for(int i=0;i<sz;++i){
dfs(v[x][i]);
}
}
int lca(int x,int y){
if(dep[x]>dep[y]){
swap(x,y);
}
int tmp=dep[y]-dep[x],ans=0;
for(int j=0;tmp;++j,tmp>>=1){
if(tmp&1){
y=fa[y][j];
}
}
if(y==x){
return x;
}
for(int j=30;j>=0&&y!=x;--j){
if(fa[x][j]!=fa[y][j]){
x=fa[x][j];
y=fa[y][j];
}
}
return fa[x][0];
}
bool cmp(int x,int y){
return dfn[x]<dfn[y];
}
int main(){
register int i,j;
in(n);
for(i=1;i<=n;++i){
in(k);
if(k<1){
t=0;
}
else{
in(t);
}
for(j=2;j<=k;++j){
in(x);
t=lca(t,x);
}
v[t].push_back(i);
fa[i][0]=t;
dep[i]=dep[fa[i][0]]+1;
for(int j=1;j<31;++j){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
dfs(0);
in(q);
for(i=1;i<=q;++i){
in(t);
if(t==0){
printf("0\n");
continue;
}
for(j=1;j<=t;++j){
in(a[j]);
}
sort(a+1,a+t+1,cmp);
ans=dep[a[1]];
for(j=2;j<=t;++j){
ans+=dep[a[j]]-dep[lca(a[j-1],a[j])];
}
printf("%d\n",ans);
}
}