[AGC008F] Black Radius

\(S(u,d)\) 表示与 \(u\) 的距离不大于 \(d\) 的点构成的点集。

为了方便后面的讨论,先加入全集的贡献 \(1\)

当所有点均可选时,考虑如何不重的计算点集,

有些题解写的是: \(\forall u\not=v , S(u,d_u)=S(v,d_v) \rightarrow d_u \not= d_v\),个人感觉是错的

结论应该是:相同的 \(S(u,d)\)\(d\) 最小的 \(u\) 唯一。

证明:
to be continued...

那么可以在相同点集时在最小的 \(d\) 对应的点 \(u\) 计算答案。

\(mx1_u\) 表示离 \(u\) 最远的点(最长链),显然有 \(d_u < mx1_u\) (不能覆盖整棵树)

其次,若有 \(S(u,d)=S(v,d-1)\) , 说明 \(u\) 的其它子树的深度 \(\le d-3\)

也就是说,对于任意 \(v\) 都有 \(d \le 1+\max_{w \not= v} mx1_w+1\),当 \(v\) 所在子树即为最深子树时限制最紧,为次长链长度,记为 \(mx2_u\)

最终得到: \(d_u \le \min(mx1_u-1,mx2_u+1)\)


现在考虑部分点不可选,我们仍在 \(d\) 最小的点计算答案(即使 \(u\) 不可选)

现在需要保证 \(S(u,d_u)\) 可以对应另一个 \(S(p,d_p)\) ,其中 \(p\) 是可选的。

充要条件是: \(S(u,d_u)\) 包含 \(u\) 的包含 \(p\) 的子树中的所有节点。

证明:

  • 充分性
    反证,若存在子树 \(t\) 没有被包含,显然 \(d_3>d_1\)
    可知 \(S(u,d_2+d_3)=S(Z,d3)\) , 矛盾
  • 必要性
    \(d_p\)\(d_u-1\) 便是一个合法的解。

可以使用换根 dp 解决。

#include <bits/stdc++.h>
using namespace std;
#define ll long long

const int MAXN = 2e5 , Inf = 0x3f3f3f3f;
int n; char s[ MAXN + 5 ];
vector< int > Graph[ MAXN + 5 ];

ll ans;

int siz[ MAXN + 5 ] , d[ MAXN + 5 ]; //覆盖关键点的子树的最小 d
int mx1[ MAXN + 5 ] , mx2[ MAXN + 5 ]; //最长链,次长链
void dfs1( int u , int fa ) {
    if( s[ u ] == '1' ) d[ u ] = 0 , siz[ u ] = 1;
    else d[ u ] = Inf;
    for( int v : Graph[ u ] ) if( v != fa ) {
        dfs1( v , u ); siz[ u ] += siz[ v ];
        if( mx1[ u ] < mx1[ v ] + 1 ) mx2[ u ] = mx1[ u ] , mx1[ u ] = mx1[ v ] + 1;
        else if( mx2[ u ] < mx1[ v ] + 1 ) mx2[ u ] = mx1[ v ] + 1;
        if( siz[ v ] ) d[ u ] = min( d[ u ] , mx1[ v ] + 1 );
    }
}
void dfs2( int u , int fa ) {
    int up = min( mx2[ u ] + 1 , mx1[ u ] - 1 );
    if( d[ u ] <= up ) ans += up - d[ u ] + 1;
    for( int v : Graph[ u ] ) if( v != fa ) {
        int lu = ( mx1[ u ] == mx1[ v ] + 1 ) ? mx2[ u ] + 1 : mx1[ u ] + 1;
        if( mx1[ v ] < lu ) mx2[ v ] = mx1[ v ] , mx1[ v ] = lu; 
        else if( mx2[ v ] < lu ) mx2[ v ] = lu;
        if( siz[ 1 ] - siz[ v ] ) d[ v ] = min( d[ v ] , lu );
        dfs2( v , u );
    }
}

int main( ) {
    // freopen("fire.in","r",stdin);
    // freopen("fire.out","w",stdout);

    scanf("%d",&n);
    for( int i = 1 , u , v ; i < n ; i ++ ) {
        scanf("%d %d",&u,&v);
        Graph[ u ].push_back( v );
        Graph[ v ].push_back( u );
    }
    scanf("%s", s + 1 );
    dfs1( 1 , 0 ); dfs2( 1 , 0 );
    printf("%lld\n", 1 + ans );
    return 0;
}
posted @ 2022-10-25 17:20  chihik  阅读(11)  评论(0编辑  收藏  举报