[AGC008F] Black Radius

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

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

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

有些题解写的是: uv,S(u,du)=S(v,dv)dudv,个人感觉是错的

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

证明:
to be continued...

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

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

其次,若有 S(u,d)=S(v,d1) , 说明 u 的其它子树的深度 d3

也就是说,对于任意 v 都有 d1+maxwvmx1w+1,当 v 所在子树即为最深子树时限制最紧,为次长链长度,记为 mx2u

最终得到: dumin(mx1u1,mx2u+1)


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

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

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

证明:

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

可以使用换根 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 @   chihik  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示