题目大意
给定一棵
n
个点的树和
Data Constraint
n,q≤100000,∑k≤100000
题解
先考虑无解的情况,显然就是存在两个关键点相邻。
如果有解那我们怎么求答案呢?可以考虑贪心。
- 如果当前点是关键点,那么它所有子树内有关键点的儿子都要被删除。
- 如果当前点不是关键点,若存在两个以上的儿子的子树都含有关键点的话。
这样做时间复杂度是
O(qn)
的,不能接受。
考虑虚树。
虚树
虚树的思想很简单,就是去除无关的点,只保留关键点以及它们的LCA,这样就能保留关键点之间的信息了。具体流程:
- 将关键点按照DFS序排序。
- 求出相邻两个关键点的LCA(只有相邻两点的LCA是有用的),添加到序列中。
- 将新得到的序列按照DFS排序。
- 去除重复的点。
- 建图。
具体建图的时候,就维护一个栈,对于当前点,如果它不在栈顶的点的子树内,我们就一直退栈,然后栈顶向当前点连边即可。
这一题套上虚树之后就可以解决了。
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std ;
#define N 100000 + 10
const int MAXN = 18 ;
stack < int > St ;
int f[N][MAXN] ;
bool flag[N] , s[N] ;
int Node[2*N] , Next[2*N] , Head[N] , tot ;
int DFN[N] , R[N] , Deep[N] , a[N] ;
int n , Q , Cnt ;
bool cmp( int i , int j ) { return DFN[i] < DFN[j] ; }
void link( int u , int v ) {
Node[++tot] = v ;
Next[tot] = Head[u] ;
Head[u] = tot ;
}
void DFS( int x ) {
DFN[x] = R[x] = ++ Cnt ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( Node[p] == f[x][0] ) continue ;
f[Node[p]][0] = x ;
Deep[Node[p]] = Deep[x] + 1 ;
DFS( Node[p] ) ;
R[x] = R[Node[p]] ;
}
}
bool Check() {
for (int i = 1 ; i <= a[0] ; i ++ ) {
if ( flag[f[a[i]][0]] ) return 0 ;
}
return 1 ;
}
int LCA( int x , int y ) {
if ( Deep[x] < Deep[y] ) swap( x , y ) ;
for (int i = MAXN - 1 ; i >= 0 ; i -- ) {
if ( Deep[f[x][i]] >= Deep[y] ) x = f[x][i] ;
}
if ( x == y ) return x ;
for (int i = MAXN - 1 ; i >= 0 ; i -- ) {
if ( f[x][i] != f[y][i] ) x = f[x][i] , y = f[y][i] ;
}
return f[x][0] ;
}
void Unique() {
int tot = 0 , las = 0 ;
for (int i = 1 ; i <= a[0] ; i ++ ) {
if ( a[i] == las ) continue ;
a[++tot] = a[i] ;
las = a[i] ;
}
a[0] = tot ;
}
bool Belong( int x , int fa ) {
return DFN[x] >= DFN[fa] && DFN[x] <= R[fa] ;
}
void MakeGraph() {
tot = 0 ;
while ( !St.empty() ) St.pop() ;
for (int i = 1 ; i <= a[0] ; i ++ ) {
Head[a[i]] = 0 ;
while ( !St.empty() && !Belong( a[i] , St.top() ) ) St.pop() ;
if ( !St.empty() ) link( St.top() , a[i] ) ;
St.push( a[i] ) ;
}
}
int Calc( int x ) {
int ret = 0 , size = 0 ;
for (int p = Head[x] ; p ; p = Next[p] ) {
ret += Calc( Node[p] ) ;
size += s[Node[p]] ;
}
if ( flag[x] ) {
s[x] = 1 ;
ret += size ;
} else {
if ( size > 1 ) ret ++ ;
s[x] = ( size == 1 ) ;
}
return ret ;
}
void Clear() {
for (int j = 1 ; j <= a[0] ; j ++ ) flag[a[j]] = 0 ;
}
int main() {
scanf( "%d" , &n ) ;
for (int i = 1 ; i < n ; i ++ ) {
int u , v ;
scanf( "%d%d" , &u , &v ) ;
link( u , v ) ;
link( v , u ) ;
}
Deep[1] = 1 ;
DFS( 1 ) ;
for (int j = 1 ; j < MAXN ; j ++ ) {
for (int i = 1 ; i <= n ; i ++ ) f[i][j] = f[f[i][j-1]][j-1] ;
}
scanf( "%d" , &Q ) ;
for (int i = 1 ; i <= Q ; i ++ ) {
int m ;
scanf( "%d" , &m ) ;
a[0] = 0 ;
for (int j = 1 ; j <= m ; j ++ ) {
int x ;
scanf( "%d" , &x ) ;
a[++a[0]] = x ;
flag[x] = 1 ;
}
sort( a + 1 , a + a[0] + 1 , cmp ) ;
if ( !Check() ) { printf( "-1\n" ) ; Clear() ; continue ; }
for (int j = 1 ; j < m ; j ++ ) a[++a[0]] = LCA( a[j] , a[j+1] ) ;
sort( a + 1 , a + a[0] + 1 , cmp ) ;
Unique() ;
MakeGraph() ;
printf( "%d\n" , Calc( a[1] ) ) ;
Clear() ;
}
return 0 ;
}
以上.