HDU 5927 Auxiliary Set DFS好题

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5927

题意:给你一颗树,然后把树上一些点染黑,其它点为白点,问白点和白点的最近公共祖先有多少个,最近公共祖先可以是黑点。

已经知道了黑点的个数m,那么白点的个数就是树上节点的个数n减去m。所以这个题目主要是求有多少个黑点是白点的最近公共祖先。

首先我们看一个黑点的子树,我们定义一个全部由黑点组成的子树叫黑子树

那么如果一个黑点有两个以上的子树不是黑子树的话 这个黑点就一定是某对或某几对白点的最近公共祖先,但是我们在整颗树上进行dfs的话就会T,所以我们可以这样考虑:

如果一个点是黑点我们就检查这个点,这个点的一个儿子如果是黑点,我们就考虑这个点为根的子树是不是黑子树,最后如果这个点的黑子树个数 与 子树个数的差大于等于2 那么 这个黑点就是白点最近公共祖先, 如果黑子树个数等于子树个数 那么这个黑点为根的子树为黑子树,否则不为黑子树

这样看来我们就只用建一个只有黑点的图,图上的边只连接两个黑点即可

然后再DFS的时候如果我们按照1~n的顺序显然是会在某颗根节点编号大,子孙节点编号小的子树上计算重复,所以得按照节点在原树上的深度从小到大排序,再来DFS

    最后这是一道好题:

    代码:
#include <bits/stdc++.h>
#define sf scanf
#define pf printf

using namespace std;
const int maxn = 100000 + 50;
struct EDGE{
    int v,pre;
}Es[maxn * 2];
int head[maxn],tot;
void EDGE_INIT(){
    memset(head,-1,sizeof head),tot = 0;
}
void EDGE_INTSERT(int u,int v){
    Es[tot].v = v;
    Es[tot].pre = head[u];
    head[u] = tot++;
}

int fa[maxn],ch[maxn],dep[maxn];
void GET_INFO(int u,int f){
    fa[u] = f;ch[u] = 0;dep[u] = dep[f] + 1;
    for(int i = head[u];~i;i = Es[i].pre){
        int v = Es[i].v;
        if(v == fa[u]) continue;
        ch[u]++;GET_INFO(v,u);
    }
}
bool cmp(const int& a,const int& b){
    return dep[a] < dep[b];
}


vector<int> Adj[maxn];
int ans;
bool vis[maxn];
bool DFS(int u){
    int cnt = 0;vis[u] = 1;
    for(int i = 0;i < Adj[u].size();++i){
        cnt += DFS(Adj[u][i]);
    }
    if(ch[u] - cnt >= 2) ans++;
    if(cnt == ch[u]) return true;
    else return false;
}

int A[maxn];
bool isBlack[maxn];
int main(){
    int T,ca = 0;sf("%d",&T);
    while( T-- ){
        int n,q;sf("%d %d",&n,&q);
        EDGE_INIT();
        for(int i = 0;i < n - 1;++i){
            int u,v;sf("%d %d",&u,&v);
            EDGE_INTSERT(u,v);
            EDGE_INTSERT(v,u);
        }dep[0] = 0;
        GET_INFO(1,0);

        pf("Case #%d:\n",++ca);
        memset(isBlack,0,sizeof isBlack);
        while(q--){
            int m;sf("%d",&m);
            for(int i = 1;i <= m;++i) sf("%d",A + i);
            for(int i = 1;i <= m;++i){
                Adj[A[i]].clear();
                isBlack[A[i]] = 1;
                vis[A[i]] = 0;
            }
            for(int i = 1;i <= m;++i){
                if(isBlack[ fa[A[i]] ]){
                    Adj[fa[A[i]]].push_back(A[i]);
                }
            }
            ans = 0;
            sort(A + 1,A + 1 + m,cmp);
            for(int i = 1;i <= m;++i){
                if(!vis[ A[i] ]){
                    DFS(A[i]);
                }
            }
            pf("%d\n",n - m + ans);
            for(int i = 1;i <= m;++i) isBlack[ A[i] ] = 0;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值