LGP8352 [SDTS 2022||SXTS 2022] 小N的独立集 学习笔记
LGP8352 [SDTS 2022||SXTS 2022] 小N的独立集 学习笔记
题意简述
给定一棵 \(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;
}
浙公网安备 33010602011771号