HDU-5927-Auxiliary Set(树上DP+xjb操作)

本文探讨了一种针对树结构的算法,旨在解决基于特定条件的辅助集大小查询问题。给定一棵树和一系列操作,文章详细介绍了如何通过深度优先搜索(DFS)和节点深度信息来高效地确定哪些节点属于辅助集,即那些自身重要或作为两个不同重要节点的最近公共祖先的节点。

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

Given a rooted tree with n vertices, some of the vertices are important. 
An auxiliary set is a set containing vertices satisfying at least one of the two conditions: 
∙∙It is an important vertex 
∙∙It is the least common ancestor of two different important vertices. 
You are given a tree with n vertices (1 is the root) and q queries. 
Each query is a set of nodes which indicates the unimportant vertices in the tree. Answer the size (i.e. number of vertices) of the auxiliary set for each query. 

Input

The first line contains only one integer T (T≤1000T≤1000), which indicates the number of test cases. 
For each test case, the first line contains two integers n (1≤n≤1000001≤n≤100000), q (0≤q≤1000000≤q≤100000). 
In the following n -1 lines, the i-th line contains two integers ui,vi(1≤ui,vi≤n)ui,vi(1≤ui,vi≤n)indicating there is an edge between uiuii and vivi in the tree. 
In the next q lines, the i-th line first comes with an integer mi(1≤mi≤100000)mi(1≤mi≤100000)indicating the number of vertices in the query set.Then comes with mi different integers, indicating the nodes in the query set. 
It is guaranteed that ∑qi=1mi≤100000∑i=1qmi≤100000. 
It is also guaranteed that the number of test cases in which n≥1000n≥1000  or ∑qi=1mi≥1000∑i=1qmi≥1000 is no more than 10. 

Output

For each test case, first output one line "Case #x:", where x is the case number (starting from 1). 
Then q lines follow, i-th line contains an integer indicating the size of the auxiliary set for each query. 

Sample Input

1
6 3
6 4
2 5
5 4
1 5
5 3
3 1 2 3
1 5
3 3 1 4

Sample Output

Case #1:
3
6
3

Hint


         
  

For the query {1,2, 3}:
•node 4, 5, 6 are important nodes For the query {5}:
•node 1,2, 3, 4, 6 are important nodes
•node 5 is the lea of node 4 and node 3 For the query {3, 1,4}:
• node 2, 5, 6 are important nodes 

题意:给定一棵树,1为根节点,给定q个操作,每个操作都是把一个集合中的节点变成不重要节点,节点外的都为重要节点,但是如果集合内的某个点是集合外某两个点的最近公共祖先,那么这个集合内的点也是重要节点,要你输出对于每次操作,有多少个重要节点

题解:这题一直在想着用LCA搞,然后又怕T,听队友说按照深度搞,最后队友做出来了(Tql.......),就是从1节点开始跑一边dfs求出每个点的深度,记录每个节点的父节点,直系子节点个数,对于当前集合内的点,我按照深度从大到小排序,深度大的在下面,先看深度大的子节点,如果子节点个数大于等于2个,那么就说明这个点也可以变成重要节点,则ans++;如果只有一个子节点就不做处理,没有子节点就把这个节点删掉(其实就是让父节点的子节点个数减1)这时候父节点可能由原来的2个子节点变成了1个子节点了,这时候是刚好不满足重要节点的,很多人会想如果他有两个子节点,ans++,不对他删除,他的父节点怎么办,实际上就是如果他的父节点也有两个子节点,其中一个就包括它,但是它还有两个子节点,那么它的子节点跟它的兄弟节点的LCA就是它的父亲,这时候也满足它的父节点个数等于2,ans++;实在不太理解画图解释吧!

第一种情况:

第二种情况:

第三种情况:

总结:这些情况还是集合中节点比较密集在一块的情况,其实在数据上,好多节点不是在一堆,那么影响集合中的点的就是集合点的子节点了,语文水平有限,实在看不懂的就看代码吧,很容易理解的,有问题欢迎指正~

代码:

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int t,n,m,q,index=0,ans;
int a[N],fa[N],son[N],sons[N],deg[N];
vector<int>v[N];
void dfs(int x,int pre)
{
	deg[x]=deg[pre]+1;
	for(int i=0;i<v[x].size();i++)
	{
		int nex=v[x][i];
		if(!deg[nex])
		{
			fa[nex]=x;
			dfs(nex,x);
			son[x]++;
		}
	}
	return ;
}
bool cmp(int i,int j)
{
	return deg[i]>deg[j];
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		memset(deg,0,sizeof(deg));
		memset(son,0,sizeof(son));
		memset(fa,0,sizeof(fa));
		scanf("%d%d",&n,&q);
		for(int i=1;i<=n;i++)v[i].clear();
		for(int i=1;i<n;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			v[x].push_back(y);
			v[y].push_back(x);
		}
		dfs(1,0);
		printf("Case #%d:\n",++index);
		while(q--)
		{
			scanf("%d",&m);
			ans=n-m;
			for(int i=1;i<=m;i++)scanf("%d",&a[i]);
			sort(a+1,a+m+1,cmp);
			// for(int i=1;i<=m;i++)cout<<son[a[i]]<<" ";cout<<endl;
			for(int i=1;i<=m;i++)sons[a[i]]=son[a[i]];
			for(int i=1;i<=m;i++)
			{
				if(sons[a[i]]>=2)ans++;
				else if(sons[a[i]]==0)sons[fa[a[i]]]--;
			}
			printf("%d\n",ans);
		}
	}
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值