CF-461-B-树形DP

461-B 题目大意

给定一棵n个节点的树,节点编号从0开始,每个节点要么为白色要么为黑色,你需要删除一些边,使得剩下的各个连通块中有且仅有一个黑色节点。

问有多少种删边方案数,答案对109+1取模。


Solution

考虑树形DP,令dp[x][0/1]表示节点x属于无黑色节点/有黑色节点的连通块的方案数,节点yx的儿子节点,初始化dp[x][col[x]]=1

对于dp[x][1],有三种情况:

  • x属于有黑色节点的连通块,y属于有黑色节点的连通块,删去两点之间的边。
  • x属于有黑色节点的连通块,y属于没有黑色节点的连通块,无需删边。
  • x属于没有黑色节点的连通块,y属于有黑色节点的连通块,无需删边。

对于dp[x][0],有两种情况:

  • x属于没有黑色节点的连通块,y属于有黑色节点的连通块,删去两点之间的边。
  • x属于没有黑色节点的连通块,y属于没有有黑色节点的连通块,无需删边。

那么转移方程如下:

dp[x][1]=dp[x][1]dp[y][0]+dp[x][1]dp[y][1]+dp[x][0]dp[y][1]

dp[x][0]=dp[x][0]dp[y][0]+dp[x][0]dp[y][1]

要注意的是dp[x][1]的转移受dp[x][0]的影响,所以要先进行前者的转移,时间复杂度O(n)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;

const int mod=1e9+7;

void solve(){
    int n;
    cin>>n;
    vector<vector<int>> e(n);
    for(int i=1;i<n;i++){
        int x;
        cin>>x;
        e[x].push_back(i);
    }
    vector<array<ll,2>> dp(n);
    for(int i=0;i<n;i++){
        int col;
        cin>>col;
        dp[i][col]=1;
    }
    function<void(int)> dfs=[&](int x){
        for(auto y:e[x]){
            dfs(y);
            dp[x][1]=((dp[x][1]*dp[y][1]%mod)+(dp[x][1]*dp[y][0]%mod)+(dp[x][0]*dp[y][1]%mod))%mod;
            dp[x][0]=((dp[x][0]*dp[y][1]%mod)+(dp[x][0]*dp[y][0]%mod))%mod;
        }
    };
    dfs(0);
    cout<<dp[0][1];
}

int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    int T=1;
    //cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
posted @   fengxue-K  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示