1 条题解
-
0
自动搬运
来自洛谷,原作者为

nosta
星空の下はダンスホール搬运于
2025-08-24 22:04:46,当前版本为作者最后更新于2019-03-22 20:41:38,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
【题意】定义有根树上一个结点的权值等于整个子树内的节点权值和,现给出个节点的权值,限制每棵树高度不超过,求满足要求的带权森林个数。
【做法】考虑状压dp,设为按排序小大排序后前个节点所构成的森林中,高度为的树集合为的方案数,转移有
$$f[i,j]\to f[i+1,j-k] \; (k\subseteq j,k\not=\varnothing,w_{i+1}=\sum_{e\in k}w_e) $$即考虑让作为高度为或的树的树根。注意到当时,存在让作为中唯一点的儿子的方案。显然仅当单调不降时能取到所有决策。
【复杂度的推导】
$$\sum_{i=0}^{2^n-1}2^{\mathbb{popcount}_i} =\sum_{i=0}^n(n,i)2^i=\sum_{i=0}^n(n,i)1^{n-i}2^i=(1+2)^n=3^n $$总时间复杂度为,需要剪枝。
#include <bits/stdc++.h> using namespace std; const int mod=1e9+7; int n,w[15],sum[1<<15]; int lmt,f[16][1<<15]; inline int lbt(int x) {return x&-x;} inline void add(int&x,int y) {if((x+=y)>=mod) x-=mod;} int main() { scanf("%d",&n); for(int i=0; i<n; ++i) scanf("%d",w+i); sort(w,w+n); for(int i=0; i<n; ++i) sum[1<<i]=w[i]; lmt=1<<n; for(int i=0; i<lmt; ++i) { if(i!=lbt(i)) sum[i]=sum[i-lbt(i)]+sum[lbt(i)]; } f[0][0]=1; for(int i=0; i<n; ++i) { for(int j=0; j<lmt; ++j) if(f[i][j]) { add(f[i+1][j|(1<<i)],f[i][j]); for(int k=j; k; k=(k-1)&j) if(w[i]==sum[k]) { add(f[i+1][j^k],f[i][j]); if(k==lbt(k)) add(f[i+1][j^k],f[i][j]); } } } int ans=0; for(int i=0; i<lmt; ++i) { add(ans,f[n][i]); } printf("%d\n",ans); return 0; }
- 1
信息
- ID
- 3779
- 时间
- 200ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者