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;
}

 

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