题解 CF735E 【Ostap and Tree】
思路来自@skylee,但他呀讲得不太对的亚子。
不关心 是怎么想到的 的话可跳转至Part2的DP部分。
Part 1
树形DP。定义着色点为着色点,合法点为与最近的着色点距离不超过
考虑要加上哪些状态。DP合并时,子树与子树之间有哪些相互作用呢?无非是一棵子树
所以我们设
Part 2
我们不满足于这个不优美的做法,考虑对其进行优化。
有一个性质: 两棵非合法子树合并后,最深的非合法点不可能被消去 。
证明:
假设我们要合并
其中
因为
如果合并后
这个性质告诉我们,对于非合法子树,
定义新的DP状态
当
当
答案为
一个孤点的DP值为
考虑如何合并
- 当
i\le k,j\le k 时,合并为f_{x,min(i,j+1)} ; - 当
i\le k,j> k,i+j-k\le k 时,x 内的染色点可以贡献到y 内的非合法点,所以合并为f_{x,i} ; - 当
i\le k,j> k,i+j-k> k 时,合并为f_{x,j+1} ; - 当
i> k,j> k 时,合并为f_{x,max(i,j+1)} ;
于是就得到了代码中神奇的一句话转移:
t[i+j<=2*k?min(i,j+1):max(i,j+1)]+=f[x][i]*f[y][j]
时间复杂度
Comment:Part1 大概是 2500 的亚子,Part2 可能有 3000?最扯的是这个 2500 的DP都可以黑啊。
#include<bits/stdc++.h>
#define mk make_pair
#define pk push_back
using namespace std;
typedef long long LL;
typedef pair<int,int> pi;
template<class T> bool cmax(T &x,T y){return y>x?x=y,1:0;}
template<class T> bool cmin(T &x,T y){return y<x?x=y,1:0;}
const int N=105,K=45,mod=1e9+7;
int n,k,f[N][K],t[K];
vector<int> to[N];
void dfs(int x,int pre){
f[x][0]=1,f[x][k+1]=1;
for(auto y:to[x])if(y^pre){
dfs(y,x);
memset(t,0,sizeof t);
for(int i=0;i<=2*k;++i)
for(int j=0;j<=2*k;++j)
(t[i+j<=2*k?min(i,j+1):max(i,j+1)]+=1ll*f[x][i]*f[y][j]%mod)%=mod;
memcpy(f[x],t,sizeof t);
}
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>k;
for(int i=1,x,y;i<n;++i) cin>>x>>y,to[x].pk(y),to[y].pk(x);
dfs(1,0);
int ans=0;
for(int i=0;i<=k;++i) (ans+=f[1][i])%=mod;
cout<<ans<<endl;
return 0;
}