zoukankan      html  css  js  c++  java
  • Codeforces 1146F. Leaf Partition(树形DP)

    Codeforces 1146F. Leaf Partition

    题目大意

    • 给出一棵大小为 N N N的有根树,把所有叶子节点分成若干集合,求使得每个集合分别连成的最小连通块之间互不相交的划分方案数。
    • N ≤ 1 0 5 N≤10^5 N105

    题解

    • 一眼看到这题,想到了LCA,想到了DFS序……
    • 其实正解完全没这么复杂,只是简单的树形DP。
    • f [ x ] [ 0 / 1 ] f[x][0/1] f[x][0/1]表示节点 x x x向上延伸或不延伸的方案数(延伸是为了和其他子树内的叶子节点构成连通块),
    • 乍一看它的儿子节点是否延伸对它是没有影响的,每个点的方案数就是所有儿子方案数的乘积,但事实上不是,
    • 若该节点不延伸的话,它就不能只有一个儿子向上延伸,因为没有其他儿子和它匹配,但也可以所有儿子都没有向上延伸;
    • 若该节点延伸的话,它就不能没有儿子向上延伸,因为它至少需要带上一个儿子继续去找其他子树匹配。
    • 所以状态转移方程就很好写了,转移过程中会涉及到除法,直接用费马小定理求逆元即可。

    代码

    #include<cstdio> 
    #include<cstring>
    using namespace std;
    #define N 200010
    #define md 998244353
    #define ll long long
    int to[N], nxt[N], last[N], len = 0;
    ll f[N][2];
    void add(int x, int y) {
    	to[++len] = y;
    	nxt[len] = last[x];
    	last[x] = len;
    }
    ll ksm(ll x, ll y) {
    	if(!y) return 1;
    	ll l = ksm(x, y / 2);
    	if(y % 2) return l * l % md * x % md;
    	return l * l % md;
    }
    void dfs(int k) {
    	ll s = 1, s0 = 1;
    	for(int i = last[k]; i; i = nxt[i]) {
    		int x = to[i];
    		dfs(x);
    		s = s * (f[x][0] + f[x][1]) % md;
    		s0 = s0 * f[x][0] % md;
    	}
    	ll s1 = 0;
    	for(int i = last[k]; i; i = nxt[i]) {
    		int x = to[i];
    		s1 = (s1 + s0 * ksm(f[x][0], md - 2) % md * f[x][1]) % md;
    	}
    	f[k][0] = (s - s1 + md) % md;
    	f[k][1] = (s - s0 + md) % md;
    	if(!last[k]) f[k][1] = f[k][0] = 1;
    }
    int main() {
    	int n, i, x;
    	scanf("%d", &n);
    	for(i = 2; i <= n; i++) {
    		scanf("%d", &x);
    		add(x, i);
    	}
    	dfs(1);
    	printf("%lld
    ", f[1][0]);
    	return 0;
    }
    
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    Linux基础篇之软件源码包安装
    11-1 网络协议和管理
    bash-2 httpd服务的源码编译安装脚本
    8-1 文本三级剑客之sed
    9-3 磁盘存储与分区
    9-2 yum,dnf和apt
    9-1 软件包管理
    bash-1 初始化CentOS系统的初始化脚本
    3-3 man手册介绍
    5-3 文件权限
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910026.html
Copyright © 2011-2022 走看看