题解[ABC207F Tree Patrolling]
题目
Sol
这道题非常的好。
题目大意:给你一棵\(n\)个点的树,在一个点设立守卫能够覆盖到所有与这个点相连的点,问所有的\(2^n\)种设立守卫的方案中,覆盖到\(k\)个点的方案有多少个。
对于所有\(k\in[1,n]\),求出答案。
Step1设状态
设\(f[0][i][j]\)为在\(i\)节点的子树中有\(j\)个点被覆盖,且节点\(i\)未设立守卫且未被覆盖的方案数。
设\(f[1][i][j]\)为在\(i\)节点的子树中有\(j\)个点被覆盖,且节点\(i\)设立守卫。
设\(f[2][i][j]\)为在\(i\)节点的子树中有\(j\)个点被覆盖,且节点\(i\)未设立守卫且被覆盖的方案数。
Step2边界条件
\(f[0][i][0]=f[1][st][1]=1\)
放则有一个点,不放就没有点。
Step3考虑转移
这一部分不难,代码很好理解,多想一想就可以写出来。
有一点点繁琐。
//p:父亲节点子树内的被看守的点的个数
//q:儿子节点子树内的被看守的点的个数
(f[0][st][p+q]+=f[0][st][p]*f[0][ed][q]%P)%=P;
(f[0][st][p+q]+=f[0][st][p]*f[2][ed][q]%P)%=P;
(f[1][st][p+q]+=f[1][st][p]*f[1][ed][q]%P)%=P;
(f[1][st][p+q]+=f[1][st][p]*f[2][ed][q]%P)%=P;
(f[2][st][p+q]+=f[2][st][p]*f[1][ed][q]%P)%=P;
(f[2][st][p+q]+=f[2][st][p]*f[0][ed][q]%P)%=P;
(f[2][st][p+q]+=f[2][st][p]*f[2][ed][q]%P)%=P;
(f[2][st][p+q+1]+=f[0][st][p]*f[1][ed][q]%P)%=P;//新增节点st
(f[1][st][p+q+1]+=f[1][st][p]*f[0][ed][q]%P)%=P;//新增节点ed
Step4考虑优化
树形背包优化。
每次在更新完父节点的\(f\)之后在累加儿子节点的\(size\)。
当然每次记得清空\(f[1][st][p]\),这个值是会不断重新变化的。
Code
#include<bits/stdc++.h>
#define N (3010)
#define ll long long
#define int long long
using namespace std;
const ll P=1000000007;
int n;
ll sz[N],f[4][N][N];
vector<int>e[N];
inline void dfs(int st,int fa){
sz[st]=1,f[0][st][0]=f[1][st][1]=1;
for(int i=0;i<(int)e[st].size();i++){
int ed=e[st][i];
if(ed==fa) continue;
dfs(ed,st);
}
for(int i=0;i<(int)e[st].size();i++){
int ed=e[st][i];
if(ed==fa) continue;
for(int p=sz[st];p>=0;p--){
for(int q=sz[ed];q>=0;q--){
if(!q){
(f[1][st][p+q+1]+=f[1][st][p]*f[0][ed][q]%P)%=P;
continue;
}
(f[0][st][p+q]+=f[0][st][p]*f[0][ed][q]%P)%=P;
(f[0][st][p+q]+=f[0][st][p]*f[2][ed][q]%P)%=P;
(f[1][st][p+q]+=f[1][st][p]*f[1][ed][q]%P)%=P;
(f[1][st][p+q]+=f[1][st][p]*f[2][ed][q]%P)%=P;
(f[2][st][p+q]+=f[2][st][p]*f[1][ed][q]%P)%=P;
(f[2][st][p+q]+=f[2][st][p]*f[0][ed][q]%P)%=P;
(f[2][st][p+q]+=f[2][st][p]*f[2][ed][q]%P)%=P;
(f[2][st][p+q+1]+=f[0][st][p]*f[1][ed][q]%P)%=P;
(f[1][st][p+q+1]+=f[1][st][p]*f[0][ed][q]%P)%=P;
}
f[1][st][p]=0;
}
sz[st]+=sz[ed];
}
return;
}
signed main(){
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v),e[v].push_back(u);
}
dfs(1,0);
for(int i=0;i<=n;i++) printf("%lld\n",(f[0][1][i]+f[1][1][i]+f[2][1][i])%P);
return 0;
}