CodeForces 613D Kingdom and its Cities 虚树的简单应用

题目大意

给定一棵 n 个点的树和 q 个询问,每次询问给出 k 个关键点,求在不删除关键点的情况下,使得这 k 个点两两不连通最少需要删除几个点。无解输出-1。

Data Constraint
n,q100000,k100000

题解

先考虑无解的情况,显然就是存在两个关键点相邻。
如果有解那我们怎么求答案呢?可以考虑贪心。

  1. 如果当前点是关键点,那么它所有子树内有关键点的儿子都要被删除。
  2. 如果当前点不是关键点,若存在两个以上的儿子的子树都含有关键点的话。

这样做时间复杂度是 O(qn) 的,不能接受。
考虑虚树。


虚树

虚树的思想很简单,就是去除无关的点,只保留关键点以及它们的LCA,这样就能保留关键点之间的信息了。具体流程:

  1. 将关键点按照DFS序排序。
  2. 求出相邻两个关键点的LCA(只有相邻两点的LCA是有用的),添加到序列中。
  3. 将新得到的序列按照DFS排序。
  4. 去除重复的点。
  5. 建图。

具体建图的时候,就维护一个栈,对于当前点,如果它不在栈顶的点的子树内,我们就一直退栈,然后栈顶向当前点连边即可。


这一题套上虚树之后就可以解决了。

SRC

#include
#include
#include
#include
#include
#include
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 ;
}

以上.

你可能感兴趣的:(虚树)