HDU 5927 Auxiliary Set 最近公共祖先

Auxiliary Set

Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 964    Accepted Submission(s): 302


Problem Description
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 ( T1000 ), which indicates the number of test cases.

For each test case, the first line contains two integers n ( 1n100000 ), q ( 0q100000 ).

In the following n -1 lines, the i-th line contains two integers  ui,vi(1ui,vin)  indicating there is an edge between  ui i and  vi  in the tree.

In the next q lines, the i-th line first comes with an integer  mi(1mi100000)  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=1mi100000 .

It is also guaranteed that the number of test cases in which  n1000   or  qi=1mi1000  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
 

Source

题目大意:


给出 整棵树。给出 k 个点, 这 k 个点是不重要的点。

需要求的是一个集合的大小,满足下面两个条件可以放入这个集合中

1.  重要的点

2. 如果某个不重要点有两个儿子为 重要的点 ,那么也放入这个集合中。

输出 集合的大小。


思路:


dfs 处理好每个点有多少个儿子。

然后对于所有不重要的点,按照深度进行从大到小排序。

从深度最大的开始检索。如果当前节点的儿子数量大于 等于2  那么当前点可以放入集合中。

如果当前点 没有儿子 那么我们可以暂时删掉这个点(将其父亲 的儿子节点的数量减 1)(如果他的父节点有两个儿子,一个为重要的,一个为不重要的,并且这个不重要的

节点没有儿子节点了  那么这个父亲节点是不重要的点,虽然他有两个儿子,但是他只有一个重要的儿子)

检索完就可以了


AC代码:


#include<bits/stdc++.h>
using namespace std;
int n,m,q;
int head[200005];
int tot;
int nextt[200005];
int num[200005];
int ff[100005];
int son[100005];
int tt[100005];
int eenum[100005];
struct node
{
    int a;
    int dep;
    friend bool operator < (const node &aa,const node &b)
    {
        return aa.dep>b.dep;
    }
}haha[100005];
bool cmp(int x,int y)
{
    return haha[x].dep>haha[y].dep;
}
void add(int u,int v)
{
    num[tot]=u;
    nextt[tot]=head[v];
    head[v]=tot;
    tot++;
    num[tot]=v;
    nextt[tot]=head[u];
    head[u]=tot;
    tot++;
}
void dfs(int u,int f)
{
    ff[u]=f;
    haha[u].dep=haha[f].dep+1;
    son[u]=0;
    int t;
    for(t=head[u];t!=-1;t=nextt[t])
    {
        if(num[t]!=f)
        {
            dfs(num[t],u);
            son[u]++;
        }
    }
}
int main()
{
    int T;
    int ca=1;
    scanf("%d",&T);
    int a,b;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        tot=0;
        for(int i=0;i<=n;i++)
        {
            head[i]=-1;
        }
        haha[0].dep=0;
        for(int i=0;i<n-1;i++)
        {
            scanf("%d%d",&a,&b);
            add(a,b);
        }
        dfs(1,0);
        int ans;
        printf("Case #%d:\n", ca);
        ca++;
        while(m--)
        {
            scanf("%d",&q);
            ans=n-q;
            for(int i=0;i<q;i++)
            {
                scanf("%d",&eenum[i]);
            }
            sort(eenum,eenum+q,cmp);
            for(int i=0;i<q;i++)
            {
                tt[eenum[i]]=son[eenum[i]];
            }
            for(int i=0;i<q;i++)
            {
                if(tt[eenum[i]]>=2)
                {
                    ans++;
                }
                else if(tt[eenum[i]]==0)
                {
                    tt[ff[eenum[i]]]--;
                }
            }
            cout<<ans<<endl;
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值