题目链接: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;
}
}
}