标签:lan += tin 状态 两种 操作 简单 i++ sig
思考到树形dp,寻找某个子树上的根节点和他的儿子节点的关系。假设一个在节点u的子树上的答案ans,也就是节点u的子树里中的点构成的树,删掉的一些边后,构成的最长不超过k的链的构造方法数目。那么通过dp,u的解可以从遍历u的子节点v来推得。
在u吸收子节点们(v们)时,有两种可能,要么就是连一条边,要么就不连这条边。如果连接这条边的话。那么最长的链应该就是从u节点+1(这是(u,v)的长度)加子节点能够到的最深的距离。因为要加直径,所以还要加目前根节点到目前最深的节点的距离。
如果不连这条边,那么最长的链就是u节点已有的最长的链或者是v节点已有的最长的链。
可见在dp中为了维护答案还要再加一层“节点u为起点的最长的简单路径”dp[u][j]
每次u找一个新儿子dp的时候,dp[u][i]就是节点u为起点的最长为i的简单路径,dp[v][j]同理
for (v in son[u])
for i=[0,maxdep[i]-dep[i]] 就是u已有的状态的最深深度
for j=[0,maxdep[j]-dep[i]] 同理
不连这条边: tmpdp[u][i]=dp[u][i]dp[v][j] //其实可以保证j>k时dp[v][j]==0 因为所有的连边操作都被跳过了
连这条边:先判断能不能连:if (i+j+1<k) tmpdp[u][i]+=dp[u][i]dp[v][j]
最后更新一下dp状态就可以了。
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;++i)
#define ll long long
const int N = 5e3+10;
struct Edge{
int u,v,next;
}e[N*2];
const int mod = 998244353;
int head[N],tot;
void add(int u,int v){
e[++tot]={u,v,head[u]}; head[u]=tot;
}
int n,k;
int dep[N],maxdep[N];
int dp[N][N];
int tmp[N];
int siz[N];
void dfs(int u,int fa){
siz[u]=1;maxdep[u]=dep[u];
dp[u][0]=1;
for (int ii=head[u];ii;ii=e[ii].next)
{
int v=e[ii].v;
if(v==fa)continue;
dep[v]=dep[u]+1;
dfs(v,u);
int kk=maxdep[u]-dep[u];
FOR(i,0,max(maxdep[u],maxdep[v])-dep[u])tmp[i]=0;
FOR(i,0,kk)
{
FOR(j,0,maxdep[v]-dep[u])
{
tmp[i]=(tmp[i]+1ll*dp[u][i]*dp[v][j]%mod)%mod;
if(i+j+1>k)continue;
tmp[max(i,j+1)]=(tmp[max(i,j+1)]+1ll*dp[u][i]*dp[v][j]%mod)%mod;
}
}
maxdep[u]=max(maxdep[u],maxdep[v]);
FOR(i,0,maxdep[u]-dep[u])dp[u][i]=tmp[i];
siz[u]+=siz[v];
}
}
int ans=0;
void solve(){
dfs(1,0);
for (int i=0;i<=k;i++) ans=(ans+dp[1][i])%mod;
cout<<ans;
}
signed main(){
cin>>n>>k;
for (int i=1;i<n;i++){
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
solve();
}
标签:lan += tin 状态 两种 操作 简单 i++ sig
原文地址:https://www.cnblogs.com/asanagiyantia/p/14820693.html