[2022.10.11 模拟赛] 联通块

题意简述

给定一颗树,每个点有点权 \((a_i,b_i)\)

问满足 \(\sum a_i \le m\) 的连通块的 \(\sum b_i\) 的最大值。

\(n \le 10^3,m \le 10^4\)

分析

有一个显然的 \(\mathcal O(nm^2)\) 的树 dp,瓶颈在于合并背包。

这里有一个 trick: 连通块问题在 dfn 序上 dp 做到单点加入

选了儿子必须选父亲,可以在 dfn 序列上倒序考虑,

\(f_{i,j}\) 表示考虑 dfn 序列上后 \(i\) 个点,选出的 \(\sum a=j\)\(\sum b\) 的最大值

  • \(i\) , 那么子树随意, \(f_{i,j} = f_{i+1,j-a_{p_i}}+b_{p_i}\)

  • 不选 \(i\) ,那么子树不能选 , \(f_{i,j}=f_{i+siz_{p_i},j}\)

考虑包含根的连通块,然后删掉根并递归所有子树,可以使用点分治做到 \(\mathcal O(nm \log n)\)

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;

const int MAXN = 1000 , MAXM = 10000 , Inf = 0x3f3f3f3f;
int n , m , a[ MAXN + 5 ] , b[ MAXN + 5 ];
vector< int > Graph[ MAXN + 5 ];

int rt , als , mns , siz[ MAXN + 5 ];
bool del[ MAXN + 5 ];
void dfs1( int u , int fa ) {
    siz[ u ] = 1; int mxs = 0;
    for( int v : Graph[ u ] ) if( v != fa && !del[ v ] ) {
        dfs1( v , u ); siz[ u ] += siz[ v ];
        mxs = max( mxs , siz[ v ] );
    } mxs = max( mxs , als - siz[ u ] );
    if( !rt || mxs < mns ) rt = u , mns = mxs;
}

int cnt , seq[ MAXN + 5 ];
void dfs3( int u , int fa ) {
    seq[ ++ cnt ] = u;
    for( int v : Graph[ u ] ) if( v != fa && !del[ v ] ) dfs3( v , u );
}

int ans , dp[ MAXN + 5 ][ MAXM + 5 ];
void dfs2( int u ) {
    del[ u ] = 1;
    cnt = 0; dfs3( u , 0 );

    dp[ cnt + 1 ][ 0 ] = 0;
    for( int i = cnt ; i >= 1 ; i -- ) {
        for( int j = 0 ; j <= m ; j ++ ) {
            //选 i
            if( j + a[ seq[ i ] ] <= m )
                dp[ i ][ j + a[ seq[ i ] ] ] = max( dp[ i ][ j + a[ seq[ i ] ] ] , dp[ i + 1 ][ j ] + b[ seq[ i ] ] );
            //不选
            dp[ i ][ j ] = max( dp[ i ][ j ] , dp[ i + siz[ seq[ i ] ] ][ j ] );
        }
    }
    for( int i = 1 ; i <= m ; i ++ ) ans = max( ans , dp[ 1 ][ i ] );
    for( int i = 1 ; i <= cnt + 1 ; i ++ ) for( int j = 0 ; j <= m ; j ++ ) dp[ i ][ j ] = -Inf;

    for( int v : Graph[ u ] ) if( !del[ v ] ) {
        rt = 0; als = siz[ v ];
        dfs1( v , u ); dfs1( rt , 0 );
        dfs2( rt );
    }
}
int main( ) {
    scanf("%d %d",&n,&m);
    for( int i = 1 ; i <= n ; i ++ ) scanf("%d %d",&a[ i ],&b[ i ]);
    for( int i = 1 , u , v ; i < n ; i ++ ) {
        scanf("%d %d",&u,&v);
        Graph[ u ].push_back( v );
        Graph[ v ].push_back( u );
    }
    
    rt = 0; als = n;
    dfs1( 1 , 0 ); dfs1( rt , 0 );
    dfs2( rt );

    printf("%d\n", ans );
    return 0;
}
posted @ 2022-10-11 15:50  chihik  阅读(28)  评论(0编辑  收藏  举报