SS241126C. 树(tree)
SS241126C. 树(tree)
题意
给你一个以 \(1\) 为根的树,每个点有点权 \(v_i\)。设这棵树的点集为 \(V\),一个合法的子集 \(V' \subseteq V\),满足存在 \(p \in V'\),使得 \(V'\) 中任意两点的 LCA 都是 \(p\)。
把 \(V\) 分成若干个 \(V'\) 称为一种划分方案,一种划分方案 \(\{ V' \}\) 的价值为 \(\prod_{\{V'\}} (\sum_{i \in V'} v_i)\)。求所有合法发划分方案的价值和。答案对输入的 \(mod\) 取模。
\(n \le 100\)。
思路
黄队讲得非常仔细,可惜我太菜了。
不保证过程正确,如有误欢迎指出/bx
考虑刻画合法 \(V'\)。发现其形如有一个点 \(p\) 是 \(V'\) 中深度最浅的点,我们把它叫做集合的头,然后 \(p\) 的每棵子树里,可以选择至多 \(1\) 个点加入集合。
考虑先解决一个简单的问题,计算合法划分方案数。
考虑树形 DP,从下往上转移。要计算加入点 \(u\) 的贡献,分 \(u\) 是一个集合的头,或者是集合的一般结点来讨论。如果 \(u\) 是一个集合的头,那么 \(u\) 的每棵子树都可以选择一个或者不选点加入 \(u\) 的集合;如果 \(u\) 是一般结点,那么我们先不算贡献,把它的贡献留到处理头结点时去计算。
设 \(f_{u,s,0/1}\) 表示处理到点 \(u\),点 \(u\) 的子树里一共有多少个一般结点未被使用,点 \(u\) 是/不是头结点,的划分方案数。答案是 \(f_{1,0,1}\)。
对于 \(u\) 是头结点的情况。枚举 \(u\) 的儿子 \(v\),枚举 \(v\) 的子树有多少个未被使用的一般结点,枚举在 \(v\) 的子树中选择一个一般结点加入 \(u\) 的集合还是不选。
转移复杂度是 \(siz_u\)。注意枚举每个 \(v\) 要把上一个 \(v\) 时计算的 \(f_u\) 覆盖掉,注意转移顺序应该是 \(j\) 从大往小枚举。初始 \(f_{u,0,1}=1\)。
对于 \(u\) 是一般结点的情况,仍然枚举 \(u\) 的儿子 \(v\),枚举 \(v\) 的子树有多少个没有被使用的一般结点。初始 \(f_{u,1,0}=1\)。
应该是这样的吧。时间是 \(O(n^2)\)。
求原问题。
考虑展开那一坨连乘,相当于在每个集合里选择一个点涂黑,然后贡献是黑点点权的乘积,对每种涂色方案的贡献求和。即
仍然考虑从下往上树形 DP。考虑加入点 \(u\) 带来的影响。
设 \(f_{u,s_0,s_1,0/1}\) 表示子树 \(u\) 有 \(s_0\) 个未被使用的白点,有 \(s_1\) 个未被使用的黑点,点 \(u\) 是头结点,集合里有没有黑点,的方案的价值之和。\(g_{u,s_0,s_1}\) 表示 \(u\) 是一般结点的答案。
初值 \(f_{u,0,0,0}=1,f_{u,0,0,1}=v_u,g_{u,1,0}=1,g_{u,0,1}=v_u\)。
应该就是这些了吧。
答案就是 \(f_{1,0,0,1}\)。
code
应该不会太难写。
思维难度较高,细节不算多,就是容易打错字。。。
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace manner {
constexpr int N=105;
int n,mod;
int add(int a,int b) { return a+b>=mod ? a+b-mod : a+b; }
void _add(int &a,int b) { a=add(a,b); }
int mul(int a,int b) { return 1ll*a*b%mod; }
void _mul(int &a,int b) { a=mul(a,b); }
int val[N];
vector<int> to[N];
int s[N][N][N],f[N][N][2],g[N][N],tmpf[N][N][2],tmpg[N][N];
int siz[N];
int u,v;
void dfs(int u,int fa) {
for(int v : to[u]) if(v^fa) dfs(v,u);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
siz[u]=1;
f[0][0][0]=1, f[0][0][1]=val[u];
g[1][0]=1, g[0][1]=val[u];
for(int v : to[u]) {
if(v^fa) {
memcpy(tmpf,f,sizeof(f));
memcpy(tmpg,g,sizeof(g));
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
rep(s0,0,siz[u]) rep(s1,0,siz[u]-s0) {
rep(x,0,siz[v]) rep(y,0,siz[v]-x) {
int sv=s[v][x][y];
int fu0=mul(sv,tmpf[s0][s1][0]), fu1=mul(sv,tmpf[s0][s1][1]);
_add(f[s0+x][s1+y][0],fu0);
_add(f[s0+x][s1+y][1],fu1);
if(x) _add(f[s0+x-1][s1+y][0],mul(fu0,x)), _add(f[s0+x-1][s1+y][1],mul(fu1,x));
if(y) _add(f[s0+x][s1+y-1][1],mul(fu0,y));
_add(g[s0+x][s1+y],mul(tmpg[s0][s1],sv));
}
}
siz[u]+=siz[v];
}
}
rep(s0,0,siz[u]) rep(s1,0,siz[u]-s0) s[u][s0][s1]=add(f[s0][s1][1],g[s0][s1]);
}
void main() {
sf("%d%d",&n,&mod);
rep(i,1,n) sf("%d ",&val[i]);
rep(i,1,n-1) {
sf("%d%d",&u,&v);
to[u].push_back(v), to[v].push_back(u);
}
dfs(1,0);
pf("%d\n",f[0][0][1]);
}
}
int main() {
#ifdef LOCAL
freopen("my.out","w",stdout);
#else
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
#endif
manner :: main();
}
本文来自博客园,作者:liyixin,转载请注明原文链接:https://www.cnblogs.com/liyixin0514/p/18571024