这道题是别人来问我的时候我xjb口胡的,结果写了1.5h才AC,wtcl
首先状态定义是显然的:
d
p
[
u
]
[
i
]
[
0
/
1
]
[
0
/
1
]
dp[u][i][0/1][0/1]
dp[u][i][0/1][0/1]表示
u
u
u的子树除自己都已经保证被监听,用了
i
i
i个监听器,当前节点是否安装监听器,是否被监听
很显然的有这么几个状态转移
d
p
[
u
]
[
i
]
[
0
]
[
0
]
=
∑
d
p
[
v
]
[
j
]
[
0
]
[
1
]
∗
d
p
[
u
]
[
i
−
j
]
[
0
]
[
0
]
dp[u][i][0][0]=\sum dp[v][j][0][1]*dp[u][i-j][0][0]
dp[u][i][0][0]=∑dp[v][j][0][1]∗dp[u][i−j][0][0]
d
p
[
u
]
[
i
]
[
0
]
[
1
]
=
∑
(
d
p
[
v
]
[
j
]
[
1
]
[
1
]
+
d
p
[
v
]
[
j
]
[
0
]
[
1
]
)
∗
(
d
p
[
u
]
[
i
−
j
]
[
0
]
[
1
]
+
d
p
[
u
]
[
i
−
j
]
[
0
]
[
0
]
)
−
d
p
[
u
]
[
i
]
[
0
]
[
0
]
dp[u][i][0][1]=\sum (dp[v][j][1][1]+dp[v][j][0][1])*(dp[u][i-j][0][1]+dp[u][i-j][0][0])-dp[u][i][0][0]
dp[u][i][0][1]=∑(dp[v][j][1][1]+dp[v][j][0][1])∗(dp[u][i−j][0][1]+dp[u][i−j][0][0])−dp[u][i][0][0]
d
p
[
u
]
[
i
]
[
1
]
[
0
]
=
∑
(
d
p
[
v
]
[
j
]
[
0
]
[
0
]
+
d
p
[
v
]
[
j
]
[
0
]
[
1
]
)
∗
d
p
[
u
]
[
i
−
j
]
[
1
]
[
0
]
dp[u][i][1][0]=\sum (dp[v][j][0][0]+dp[v][j][0][1])*dp[u][i-j][1][0]
dp[u][i][1][0]=∑(dp[v][j][0][0]+dp[v][j][0][1])∗dp[u][i−j][1][0]
d
p
[
u
]
[
i
]
[
1
]
[
1
]
=
∑
(
d
p
[
v
]
[
j
]
[
1
]
[
1
]
+
d
p
[
v
]
[
j
]
[
1
]
[
0
]
+
d
p
[
v
]
[
j
]
[
0
]
[
0
]
+
d
p
[
v
]
[
j
]
[
0
]
[
1
]
)
∗
(
d
p
[
u
]
[
i
−
j
]
[
1
]
[
0
]
+
d
p
[
u
]
[
i
−
j
]
[
1
]
[
1
]
)
−
d
p
[
u
]
[
i
]
[
1
]
[
0
]
dp[u][i][1][1]=\sum (dp[v][j][1][1]+dp[v][j][1][0]+dp[v][j][0][0]+dp[v][j][0][1])*(dp[u][i-j][1][0]+dp[u][i-j][1][1])-dp[u][i][1][0]
dp[u][i][1][1]=∑(dp[v][j][1][1]+dp[v][j][1][0]+dp[v][j][0][0]+dp[v][j][0][1])∗(dp[u][i−j][1][0]+dp[u][i−j][1][1])−dp[u][i][1][0]
但是更新的顺序有点小烦
首先,因为是树形计数问题,所以DP的时候前面的状态不能保留,要开一个临时数组g更新(当然可以不用,特判一下
j
=
0
j=0
j=0的情况优先更新,再倒序更新即可)
其次,我们需要考虑每次转移时的枚举边界,否则复杂度会假:(下文
s
i
z
[
u
]
siz[u]
siz[u]均为只算入当前子树和之前已更新子树的值,详见代码)
i
i
i肯定不能超过其子树大小
0
<
=
i
<
=
m
i
n
(
s
i
z
[
u
]
,
k
)
0<=i<=min(siz[u],k)
0<=i<=min(siz[u],k)
j
j
j要满足
i
−
j
<
=
s
i
z
[
u
]
−
s
i
z
[
v
]
i-j<=siz[u]-siz[v]
i−j<=siz[u]−siz[v]且不超过
v
v
v的子树大小:
m
a
x
(
0
,
s
i
z
[
v
]
−
s
i
z
[
u
]
+
i
)
<
=
j
<
=
m
i
n
(
i
,
s
i
z
[
v
]
)
max(0,siz[v]-siz[u]+i)<=j<=min(i,siz[v])
max(0,siz[v]−siz[u]+i)<=j<=min(i,siz[v])
此外,DP时那个减法操作最后再做,以免减出负数,也方便维护转移的正确性
这样就能保证正确性了
至于复杂度……
由于我们的更新是正确的且没有冗余的(也许),所以转移复杂度能够达到
O
(
K
2
)
O(K^2)
O(K2)的点最多
n
/
k
n/k
n/k个,所以复杂度为
O
(
n
k
)
O(nk)
O(nk)
代码:
#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
inline int rd(){
int re data=0;static char ch=0;ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))data=(data<<3)+(data<<1)+(ch^48),ch=getchar();
return data;
}
const int N=1e5+5,K=105,mod=1e9+7;
int n,k,first[N],siz[N],cnt,dp[N][K][2][2],g[K][2][2];
struct edges{int v,nxt;}e[N<<1];
inline void add(int u,int v){e[++cnt]=(edges){v,first[u]},first[u]=cnt;}
inline void dfs(int u,int fa){
siz[u]=1,dp[u][0][0][0]=dp[u][1][1][0]=1;
for(int re v,i=first[u];i;i=e[i].nxt){
v=e[i].v;if(v==fa)continue;
dfs(v,u),siz[u]+=siz[v],memcpy(g,dp[u],sizeof g);
for(int re i=min(k,siz[u]);~i;--i)dp[u][i][0][0]=dp[u][i][0][1]=dp[u][i][1][0]=dp[u][i][1][1]=0;
for(int re i=min(k,siz[u]);~i;--i){
int re maxl=min(i,siz[v]);
for(int re j=max(0,i+siz[v]-siz[u]);j<=maxl;++j){
(dp[u][i][0][0]+=(ll)dp[v][j][0][1]*g[i-j][0][0]%mod)%=mod,
(dp[u][i][0][1]+=((ll)dp[v][j][1][1]+dp[v][j][0][1])%mod*(g[i-j][0][1]+g[i-j][0][0])%mod)%=mod,
(dp[u][i][1][0]+=((ll)dp[v][j][0][0]+dp[v][j][0][1])%mod*g[i-j][1][0]%mod)%=mod,
(dp[u][i][1][1]+=((ll)dp[v][j][1][1]+dp[v][j][1][0]+dp[v][j][0][0]+dp[v][j][0][1])%mod*(g[i-j][1][0]+g[i-j][1][1])%mod)%=mod;
}((dp[u][i][0][1]-=dp[u][i][0][0])+=mod)%=mod,((dp[u][i][1][1]-=dp[u][i][1][0])+=mod)%=mod;
}
}
}
signed main(){
n=rd(),k=rd();
for(int re u,v,i=1;i^n;++i)u=rd(),v=rd(),add(u,v),add(v,u);
dfs(1,0),cout<<(dp[1][k][0][1]+dp[1][k][1][1])%mod,exit(0);
}