HDU 5927 Auxiliary Set (DFS+模拟)

Auxiliary Set

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


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

题意是给定一棵树,给出q次询问,每次给定一个集合,集合中的数为不重要的数,其他数为重要数,但是如果一个不重要数的孩子有两个及两个以上的是重要数,那么这个数也是一个重要数,问最后有多少的重要数。

显然除集合内的题都是重要数,需要确定的是集合内的数是否是重要数,然后就在搞集合内的重要数,然后就花样超时,实在是搞不懂为什么花样超时,也实在搞不懂为什么随便一交就AC,莫名AC。对于不重要数,需要判断它的孩子是否有两个以上的重要数,dfs求的当前结点的 孩子数和当前结点的父结点都可以想到,但之后有一步优化,因为每次都是输入一个集合,为了确保当前结点的后效性,需要从最后一层结点向上遍历,此时需要在dfs中记录当前结点在这棵树中的层数,然后对输入的m个点进行sort排序,让后进入树的结点排在前面,优先遍历。

遍历时需要记录当前结点的父结点,如果此结点的重要孩子数>=2,则ans++,如果此结点的重要孩子数==0,那么这个点对其父节点没有贡献,则此父结点孩子数--,因为下一次要恢复孩子数,所以用另一个数组记录减去了多少孩子,最后将这些孩子的数量再加上,最后输出n-m+ans.

代码实现:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <set>
#include <map>
#define mset(a,i) memset(a,i,sizeof(a))
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const int MAX=505050;
const double eps=1e-6;
int dir[4][2]={1,0,0,1,-1,0,0,-1};
inline void SS(char*s){scanf("%s",s);}
inline ll SI(){ll x;scanf("%lld",&x);return x;}
inline ll P(ll x){printf("%lld\n",x);}
inline ll qpow(ll n,ll m){n%=mod;ll ans=1;while(m){if(m%2)
		ans=(ans*n)%mod;m/=2;n=(n*n)%mod;}return ans;}
inline ll inv(ll b){return b==1?1:(mod-mod/b)*inv(mod%b)%mod;}
inline ll inv2(ll b){return qpow(b,mod-2);}

struct node{
    int s,t,next;
}e[MAX];
int head[MAX],cnt;
void add(int u,int v)
{
    e[cnt]=node{u,v,head[u]};
    head[u]=cnt++;
}
int vis[MAX];
int fa[MAX];
int child[MAX];
int a[MAX],b[MAX],c[MAX];
int vf[MAX];
void dfs(int root,int dad,int index)
{
    c[root]=index;
    fa[root]=dad;
    child[root]=0;
    for(int i=head[root];~i;i=e[i].next)
    {
        int t=e[i].t;
        if(t!=dad)
        {
            child[root]++;
            dfs(t,root,index+1);
        }
    }
}
int cmp(int a,int b)
{
    return c[a]>c[b];
}
int main()
{
    int T,r=1,n,q,u,v,m;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&q);
        memset(head,-1,sizeof(int)*(n+2));
        cnt=0;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        printf("Case #%d:\n",r++);
        memset(vis,-1,sizeof(int)*(n+2));
        memset(vf,-1,sizeof(int)*(n+2));
        dfs(1,-1,1);
        mset(b,0);
        while(q--)
        {
            scanf("%d",&m);
            int ans=0;
            for(int i=1;i<=m;i++)
            scanf("%d",&a[i]);
            sort(a+1,a+1+m,cmp);
            for(int i=1;i<=m;i++)
            {
                int temp=fa[a[i]];
                if(temp==-1&&child[a[i]]>=2)
                ans++;
                if(temp==-1)
                continue;
                if(child[a[i]]>=2)
                ans++;
                if(child[a[i]]==0)
                {
                    child[temp]--;
                    b[temp]++;
                }
            }
            printf("%d\n",n-m+ans);
            for(int i=1;i<=m;i++)
            {
                int temp=fa[a[i]];
                if(b[temp])
                {
                    child[temp]+=b[temp];
                    b[temp]=0;
                }
            }
        }
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值