LGP8352 [SDTS 2022||SXTS 2022] 小N的独立集 学习笔记

LGP8352 [SDTS 2022||SXTS 2022] 小N的独立集 学习笔记

Luogu Link

题意简述

给定一棵 \(n\) 个结点的树。每个点将被分配一个点权 \(c_u\in [1,k]\)。对于所有 \(i\in [1,kn]\),求有多少种权值分配方案,使得树的最大权独立集大小为 \(i\)

\(n\le 10^3,k\le 5\)

做法解析

暴力做法是枚举所有权值分配方案。设 \(f_{u,0/1}\) 表示 \(u\) 子树中点 \(u\) 选/不选的最大独立集大小,做经典的树上最大权独立集 dp。时间复杂度 \(O(nk^n)\)。可以拿到 \(\text{15pts}\)

考虑 \(n\le 30\) 的点,我们发现 \(f\) 的状态数和取值范围都很小。这启发我们把 \(f_{u,0/1}\) 的值设为状态。

\(g_{u,x,y}\)\(f_{u,0},f_{u,1}\) 分别为 \(x,y\) 时的方案数。转移时考虑一个一个合并 \(u\) 的儿子 \(v\)。枚举 \(i,j,p,q\) 作为 \(f_{u,0},f_{u,1},f_{v,0},f_{v,1}\)。易得转移:\(g'_{u,i+\max(p,q),j+p}+=g_{u,i,j}\times g_{v,p,q}\)

此处给出 \(\text{50pts}\) 做法的核心代码与其对应提交记录

void dfs(int u,int f){
    siz[u]=1;
    for(int i=1;i<=K;i++)dp[u][0][i]=1;
    for(auto v : Tr[u]){
        if(v==f)continue;
        dfs(v,u);memset(tmp,0,sizeof(tmp));
        for(int i=0;i<=siz[u]*K;i++){
            for(int j=0;j<=siz[u]*K;j++){
                if(!dp[u][i][j])continue;
                for(int p=0;p<=siz[v]*K;p++){
                    for(int q=0;q<=siz[v]*K;q++){
                        tmp[i+max(p,q)][j+p]+=dp[u][i][j]*dp[v][p][q];
                    }
                }
            }
        }
        memcpy(dp[u],tmp,sizeof(tmp));
        siz[u]+=siz[v];
    };
}

我们发现这个做法里面枚举了整整四层啊,四层!的 \(nk\),而且状态数就是 \(O(n^3k^2)\) 的,对于 \(n\le 10^3\) 来说时空都爆炸了。优化到正解肯定得想办法去掉几个 \(n\) 之类的。

你说巧不巧,正好我们的树上最大权独立集还有一种 \(\texttt{DP}\) 方法:我们定义 \(f_{u,0/1}\) 表示是否强制不选 \(u\)\(u\) 子树内的最大方案大小。转移即 \(f_{u,1}\)(即强制不选的)从 \(f_{v,0}\) 转移,\(f_{u,0}\)\(f_{v,1}\) 转移。

但我们为什么要这么做呢?因为上面那个 \(\texttt{DP}\) 有一个很好的性质:\(0\le f_{u,0}-f_{u,1}\le val_u\le k\)(强制不选的答案肯定小于等于无限制的答案,且两者一定不会差出 \(u\) 点的权值)。

为什么一定不会差出 \(u\) 的权值呢?你想,强制不选的答案相当于对于所有子树选法都无限制,也就是说所有子树都已经最大化了它们的答案;你要是再把 \(u\) 也选上了,那它岂不是一定不劣于最优解?也就是说 \(f_{u,1}+val_u\ge f_{u,0}\)。移项即得上式。

这意味着我们可以把之前枚举的一个 \(nk\) 变成 \(k\)。这么一通下来反正复杂度就优化到了 \(O(n^2k^4)\)

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
using namespace omodint;
using mint=m107;
const int MaxN=1e3+5;
int N,K,X,Y;
vector<int> Tr[MaxN];
void addudge(int u,int v){
    Tr[u].push_back(v);
    Tr[v].push_back(u);
}
int siz[MaxN];
mint tmp[MaxN*5][6],dp[MaxN][MaxN*5][6];
void dfs(int u,int f){
    siz[u]=1;
    for(int i=1;i<=K;i++)dp[u][0][i]=1;
    for(auto v : Tr[u]){
        if(v==f)continue;
        dfs(v,u);memset(tmp,0,sizeof(tmp));
        for(int i=0;i<=siz[u]*K;i++){
            for(int j=0;j<=K;j++){
                if(dp[u][i][j]==0)continue;
                for(int p=0;p<=siz[v]*K;p++){
                    for(int q=0;q<=K;q++){
                        tmp[i+p+q][max(0,j-q)]+=dp[u][i][j]*dp[v][p][q];
                    }
                }
            }
        }
        memcpy(dp[u],tmp,sizeof(tmp));
        siz[u]+=siz[v];
    };
}
mint ans[MaxN];
int main(){
    readis(N,K);
    for(int i=1;i<N;i++)readis(X,Y),addudge(X,Y);
    dfs(1,0);
    for(int i=1;i<=N*K;i++)for(int j=0;j<=K;j++)ans[i+j]+=dp[1][i][j];
    for(int i=1;i<=N*K;i++)writil(miti(ans[i]));
    return 0;
}
posted @ 2025-07-25 16:20  矞龙OrinLoong  阅读(5)  评论(0)    收藏  举报