LCA+模拟 HDU 5927 Auxiliary Set

 

Describe

多组测试数据

给你n个点的树输入n-1条边后,q次询问,每次询问给你一个未知点集,点集外面的点都是已知的重要点,定义两个重要点的LCA也是重要点,问你一共有多少重要点

Solution

用到了LCA的一个性质:同一个节点的不同子树中的任何节点的LCA为该节点本身。所以我们要记录一个节点的所有儿子节点数目,然后根据未知顶点,依据深度从高到低排序,对于非重要点进行删除,然后层层向上判断,如果节点的子节点的重要节点多于两个,那么该节点为重要节点;如果是一个进行保留;如果没有;则将清理该节点的父节点;

Code

#include <bits/stdc++.h>

using namespace std;
const int maxn = 2e5 + 10;
struct node{
    int to,net;
}edge[maxn];
int id[maxn],cnt;
int fa[maxn],son[maxn],deep[maxn];
int n;

bool cmp(int u,int v){
    return deep[u] > deep[v];
}
void add(int from,int to){
    edge[cnt].to = to;
    edge[cnt].net = id[from];
    id[from] = cnt++;
}
void init(){
    memset(id,-1,sizeof(id));
    cnt = 0;
}
//更新维护了子节点的个数,节点的深度,和父节点
void dfs(int now,int dep){
    son[now] = 0;
    deep[now] = dep;
    for(int i = id[now];~i;i = edge[i].net){
        int to = edge[i].to;
        if(to != fa[now]){
            fa[to] = now;
            son[now]++;
            dfs(to,dep+1);
        }
    }
}
int p[maxn],op[maxn];
int main()
{
    int t,n,q;
    scanf("%d",&t);
    for(int cas = 1;cas <= t;++cas){
        scanf("%d%d",&n,&q);
        init();
        int u,v;
        for(int i = 1;i <= n - 1;++i){
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        fa[1] = -1;
        dfs(1,1);
        printf("Case #%d:\n",cas);
        int k;
        while(q--){
            scanf("%d",&k);
            //给出一个点集合
            for(int i = 1;i <= k;++i){
                scanf("%d",&p[i]);
                //对于每个点,可提供支持的点的数量记录
                op[p[i]] = son[p[i]];
            }
            //根据深度排序已知点集,由下到上
            sort(p+1,p+k+1,cmp);
            int ans = n - k;//一开始有n-k个重要的
            for(int i = 1;i <= k;++i){//对于k个已知点
                if(op[p[i]] >= 2)ans++;//如果他的子树中仍然存在两个以上的重要点那么他也为重要点
                else if(op[p[i]] == 0)op[fa[p[i]]]--;//如果不存在点,那么就不是重要的点
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值