题解:AT_abc036_d [ABC036D] 塗り絵

湖南省队御用绫厨TM_Sharweek

2024-11-20 22:05:34

Solution

到底是谁在做这些题的翻译怎么输入输出格式都不给一下。

题意简述

给出一棵无根树,你可以对一个点选择染或者不染,不能有两个相邻节点都染色。求总的方案数。

输入格式:

先输入一个数 n,表示这棵树上所有点的总个数。

2n 行,一行两个整数 uv,表示这两个点之间有连边。

输出格式:

输出一行一个整数,表示总方案数对 10^9+7 取模后得到的答案。

思路分析

这种奇奇怪怪数数题大概率是计数 DP 了,所以往树形计数 DP 的方向思考。

我们先构造一个十分无脑的状态:f_u 表示以 u 为根的子树的方案数。但是这个状态的转移也太难想了(或者说根本就不存在),所以我们考虑加一点状态。我们需要增加一个状态便于我们利用不能有相邻两个节点都染色这个性质转移。我们发现如果知道了一个节点 u 的所有儿子的子树的答案,我们只需要知道这些儿子的染色情况和 u 的染色情况就能非常方便的转移了。因为一个儿子的子树中所有节点到另一个儿子的子树中任意节点的路径都一定会经过 u,所以根本不可能相邻。因此我们再添加一个状态 jf_{u,j} 表示 u 的子树中 u 染色(j=1)/不染色(j=0)的合法方案数即可。

给出这个状态后,推出转移方程是十分简单的。因为每棵子树都是相对独立的,可以看作是把 u 的子树染色的几个步骤(先染某一个儿子的子树,有多少种方案;再染另一个儿子的子树,有多少种方案;……),所以根据乘法原理,就有 f_{u,1}=\prod_{v\in son_u}f_{v,0},f_{u,0}=\prod_{v\in son_u}(f_{v,1}+f_{v,0})son_u 表示 u 的儿子集合)。为什么后面那个式子中是 f_{v,1}+f_{v,0} 呢?因为这是将这棵子树染色的两类方式(你总不能先把它染成一个颜色再把它染成另一个颜色吧!),所以要使用加法原理。边界条件是 f_{u,1}=f_{u,0},其中 u 是叶子结点。因为乘法的单位元是 1,所以初始时把所有 f_{u,1}f_{u,0} 都赋值为 1 即可。答案就是 f_{root,1}+f_{root,0}root 是根节点)。

代码

以下是我的 C++ 代码

/*********************************************************************
    程序名:
    版权:
    作者:
    日期: 2024-11-20 21:43
    说明:
*********************************************************************/
#include <bits/stdc++.h>

#define m_p make_pair
#define p_b push_back
#define fst first
#define sec second

#define p_q priority_queue
#define u_map unordered_map

using namespace std;

using ll = long long;
using ull = unsigned long long;
using i128 = __int128;

const int N = 1e5 + 50;
const ll P = 1e9 + 7;

vector<int> g[N];
ll f[N][2];

void dfs(int u, int fa) {
    f[u][1] = f[u][0] = 1;
    for (int v : g[u]) {
        if (v == fa) {
            continue;
        }
        dfs(v, u);
        f[u][1] = (f[u][1] * f[v][0]) % P;
        f[u][0] = (f[u][0] * (f[v][1] + f[v][0])) % P;
    }
}

int main() {
//  freopen(".in", "r", stdin);
//  freopen(".out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        g[u].p_b(v);
        g[v].p_b(u);
    }
    dfs(1, 0);
    cout << (f[1][1] + f[1][0]) % P << endl;

    return 1007120412;
}