Neko and tree HDU - 6540 (树形dp)

题意:给你一棵树,树上有一些重要的点,让你选一个只包含重要的点的点集,满足点集里最远的两个点不超过k,问有多少种选法

想了好久,题解说得太简略了。。。

dp[i][j]表示在i这颗子树中距离i点最远点距离为j的方案数。

考虑如何从子树转移,显然 我们遍历到一颗新子树时,要用之前的所有方案乘以这颗子树的方案来更新。

即dp[u][max(i,j+1) ]+=dp[u][i]*dp[to][j]

暴力更新的复杂度是k^2的。需要用前缀和维护一下,

最后如果这个点可选,那么有选这个点和不选这个点两种情况,要乘2,然后加上只有这个点的情况。

最后计算总和的时候,如果这个点不是根,只加上长度为k的情况,因为其他情况还有可能去更新,会在他的祖宗里计算到,如果不是加上所有方案。

#include <bits/stdc++.h>

using namespace std;
#define N 5010
#define ll long long
#define mod 1000000007
#define go(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define add(a,b) ((a+=(b))%=mod)
ll dp[N][N],sum[N][N],f[N],ans;
vector<int>path[N];
int is[N];
int n,m,k,x,y;
void cal(int u,int to){
    go(i,1,k-1)f[i]=dp[u][i]*sum[to][min(i,k-i)-1]%mod;
    go(i,1,k-1)add(f[i],sum[u][min(i,k-i)]*dp[to][i-1]%mod);
    go(i,1,k/2)add(f[i],(mod-dp[u][i]*dp[to][i-1]%mod)%mod);//容斥,减去计算重复的方案
    go(i,1,k)
        add(dp[u][i],(dp[to][i-1]+f[i])%mod),//加上这颗子树的方案
        sum[u][i]=(sum[u][i-1]+dp[u][i])%mod;
}
void dfs(int u,int fa){
    for(auto to:path[u]){
        if(to==fa)continue;
        dfs(to,u);
        cal(u,to);
    }
    if(is[u]){//如果这个点可选
        sum[u][0]=dp[u][0]=1;//只选这个点
        go(i,1,k)
            add(dp[u][i],dp[u][i]),//分选它不选两种,所以要乘2
            sum[u][i]=(sum[u][i-1]+dp[u][i])%mod;//更新前缀和
    }
    add(ans,u!=1?dp[u][k]:sum[u][k]);//如果这个点是根,加所有情况,否则只加长度为k的情况
}
int main()
{

    cin>>n>>m>>k;
    go(i,2,n)
        scanf("%d%d",&x,&y),
        path[x].push_back(y),
        path[y].push_back(x);
    go(i,1,m)scanf("%d",&x),is[x]=1;
    dfs(1,0);
    cout<<ans<<endl;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章