Kattis boxes LCA

本文介绍了一种通过构建树结构解决盒子嵌套问题的方法,并详细解释了如何使用ST表进行快速查询,包括预处理和查询过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

There are NN boxes, indexed by a number from 11 to NN. Each box may (or not may not) be put into other boxes. These boxes together form a tree structure (or a forest structure, to be precise).

You have to answer a series of queries of the following form: given a list of indices of the boxes, find the total number of boxes that the list of boxes actually contain.

Consider, for example, the following five boxes.

\includegraphics[width=0.5\textwidth ]{Boxes}
Figure 1: Sample input
  • If the query is the list “1”, then the correct answer is “5”, because box 1 contains all boxes.

  • If the query is the list “4 5”, then the correct answer is “2”, for boxes 4 and 5 contain themselves and nothing else.

  • If the query is the list “3 4”, then the correct answer is “2”.

  • If the query is the list “2 3 4”, then the correct answer is “4”, since box 2 also contains box 5.

  • If the query is the list “2”, then the correct answer is “3”, because box 2 contains itself and two other boxes.

Input

The first line contains the integer NN (1N2000001≤N≤200000), the number of boxes.

The second line contains NN­integers. The iith integer is either the index of the box which contains the iith box, or zero if the iith box is not contained in any other box.

The third line contains an integer QQ (1Q1000001≤Q≤100000), the number of queries. The following QQ lines will have the following format: on each line, the first integer MM(1M201≤M≤20) is the length of the list of boxes in this query, then MM integers follow, representing the indices of the boxes.

Output

For each query, output a line which contains an integer representing the total number of boxes.

Sample Input 1Sample Output 1
5
0 1 1 2 2
5
1 1
2 4 5
2 3 4
3 2 3 4
1 2
5
2
2
4
3

题意:给出N个盒子,其中一些盒子可能会被放进另一些盒子里,Q个询问,每次询问给出M个盒子序号,求这M个盒子包含的盒子总数


题解:建树,然后从每个根节点搜出这个点的子树有多少点(包含自己)

因为m较小  所以我们可以暴力出任意两个点  一个点是否包含另一个点  如果包含  就把此点删去

寻找LCA可以用st表做  O(nlogn)预处理O(1)查询

但是题目中可能有多个根节点  我们可以建立一个虚拟点n+1  将所有的根都连向他  st表预处理做一次就可以了




#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int n,head[200005],tot,vis[200005],dpp[200005];
vector<int>root;
struct node{
	int to,nex;
}edge[800005];
void add(int u,int v){
	edge[tot].to=v;
	edge[tot].nex=head[u];
	head[u]=tot++;
}
int dfss(int t){
	vis[t]=1;
	for(int i=head[t];~i;i=edge[i].nex){
		int v=edge[i].to;
		if(!vis[v]){
			dpp[t]+=dfss(v);
		}
	}
	return dpp[t];
}
const int MAXN=200005;
int rmq[2*MAXN];//rmq数组,就是欧拉序列对应的深度序列
struct ST
{
    int mm[2*MAXN];
    int dp[2*MAXN][20];//最小值对应的下标
    void init(int n)
    {
        mm[0] = -1;
        for(int i = 1;i <= n;i++)
        {
            mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
            dp[i][0] = i;
        }
        for(int j = 1; j <= mm[n];j++)
            for(int i = 1; i + (1<<j) - 1 <= n; i++)
                dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
    }
    int query(int a,int b)//查询[a,b]之间最小值的下标
    {
        if(a > b)swap(a,b);
        int k = mm[b-a+1];
        return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
    }
};
int F[MAXN*2];//欧拉序列,就是dfs遍历的顺序,长度为2*n-1,下标从1开始
int P[MAXN];//P[i]表示点i在F中第一次出现的位置
int cnt;

ST st;
void dfs(int u,int pre,int dep)
{
    F[++cnt] = u;
    rmq[cnt] = dep;
    P[u] = cnt;
    for(int i = head[u];i != -1;i = edge[i].nex)
    {
        int v = edge[i].to;
        if(v == pre)continue;
        dfs(v,u,dep+1);
        F[++cnt] = u;
        rmq[cnt] = dep;
    }
}
void LCA_init(int root,int node_num)//查询LCA前的初始化
{
    cnt = 0;
    dfs(root,root,0);
    st.init(2*node_num-1);
}
int query_lca(int u,int v)//查询u,v的lca编号
{
    return F[st.query(P[u],P[v])];
}
int ts[25],vs[25];
int main(){
	int i,j;
	scanf("%d",&n);
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++){
		scanf("%d",&j);
		if(!j){
			add(n+1,i);
			add(i,n+1);
			root.push_back(i);
		}
		else{
			add(i,j);
			add(j,i);
		}
		dpp[i]++;
	}
	vis[n+1]=1;
	for(i=0;i<root.size();i++){
		int ds=root[i];
		dfss(ds);
	}
	LCA_init(n+1,n+1);
	int kk;
	scanf("%d",&kk);
	while(kk--){
		int dt;
		scanf("%d",&dt);
		for(i=1;i<=dt;i++){
			scanf("%d",&ts[i]);
		}
		int oo=0;
		memset(vs,0,sizeof(vs));
		for(i=1;i<=dt;i++){
			for(j=i+1;j<=dt;j++){
				int ros=query_lca(ts[i],ts[j]);
				if(ros==ts[i])vs[j]=1;
				else if(ros==ts[j]){
					vs[i]=1;
					break;
				}
			}
			if(!vs[i])oo+=dpp[ts[i]];
		}
		printf("%d\n",oo);
	}
	return 0;
} 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值