Subtree

题目大意

给定一颗树,你可以选出一些节点,你需要对于每个点求出在强制选这个点的情况下所有选择的点联通的方案数,对给定模数取模。

思路分析

对于这种求树上每一个点方案数的题目,首先考虑换根 DP。

强制钦定树根为 \(1\),设 \(f_i\) 表示在 \(i\) 的子树中选点,\(i\) 强制选,所有选择的点联通的方案数,\(g_i\) 表示在 \(i\) 的子树外选点,\(i\) 强制选,所有选择的点联通的方案数,那么显然点 \(s\) 的答案就是 \(f_s\times g_s\)。

  • 考虑计算 \(f\):

对于叶节点 \(s\),显然 \(f_s=1\),对于非叶节点,容易得出状态转移方程:

\[f_{u}=\prod_{v\in \text{son}_{u}}(f_v+1)
\]

解释一下,\(f_v+1\) 就是 \(u\) 的一个子节点的子树染色的方案数,而 \(u\) 的子树的染色方案数就是所有 \(f_v+1\) 的乘积。

  • 考虑计算 \(g\):

对于根节点 \(1\),显然 \(g_1=1\),对于非根节点,不难得出状态转移方程:

\[g_v=g_{u}\times\frac{f_{u}}{f_v+1},u=\text{fa}_{v}
\]

解释一下,从 \(g_u\) 转移到 \(g_v\),新增的节点就是 \(u\) 的子树去掉 \(v\) 的子树中的点后的所有点,而这些点染色的方案数就是 \(\frac{f_{u}}{f_{v}+1}\),也可以理解为在 \(f_u\) 中去掉所有由 \(v\) 产生的贡献。

但是直接求肯定是没法求的,模数不一定是质数,不一定存在逆元,但是我们发现我们可以将除法改为乘法,也即:

\[g_{v}=g_{u}\times\prod_{p\not =v,p\in \text{son}_u} (f_p+1)
\]

而这个可以通过预处理每个节点的子节点权值的前缀积和后缀积实现。

故我们只需要通过两遍 dfs 就可以在 \(O(n)\) 的时间空间内解决问题。

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath> using namespace std;
const int N=200200;
#define int long long int n,mod,in1,in2,idx=1;
int to[N],nxt[N],head[N];
int f[N],g[N]; vector<int> pre[N],suf[N]; void add(int u,int v){
idx++;to[idx]=v;nxt[idx]=head[u];head[u]=idx;
} void dfs_1(int s,int fa){
f[s]=1;
for(int i=head[s];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
dfs_1(v,s);
f[s]=f[s]*(f[v]+1)%mod;
pre[s].push_back(f[v]+1);
suf[s].push_back(f[v]+1);
}
for(int i=1;i<pre[s].size();i++)
pre[s][i]=pre[s][i]*pre[s][i-1]%mod;//前缀积
for(int i=suf[s].size()-2;i>=0;i--)
suf[s][i]=suf[s][i]*suf[s][i+1]%mod;//后缀积
} void dfs_2(int s,int fa){
int num=0,x=pre[s].size();
for(int i=head[s];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
num++;
if(x==1) g[v]=g[s]+1; //一些特判,可能不需要
else if(num==1) g[v]=g[s]*suf[s][num]%mod+1;
else if(num==x) g[v]=g[s]*pre[s][num-2]%mod+1;
else g[v]=g[s]*(pre[s][num-2]*suf[s][num]%mod)%mod+1;
dfs_2(v,s);
}
} signed main(){
scanf("%lld%lld",&n,&mod);
for(int i=1;i<n;i++){
scanf("%lld%lld",&in1,&in2);
add(in1,in2);add(in2,in1);
}
dfs_1(1,0);
g[1]=1;
dfs_2(1,0);
for(int i=1;i<=n;i++)
cout<<(f[i]*g[i]%mod)<<'\n';
return 0;
}

Subtree 题解的更多相关文章

  1. CF1324F Maximum White Subtree 题解

    原题链接 简要题意: 给定一棵树,每个点有黑白两种颜色:对每个节点,求出包含当前节点的连通图,使得白点数与黑点数差最小.输出这些值. F题也这么简单,咳咳,要是我也熬夜打上那么一场...可惜没时间打啊 ...

  2. Lintcode245 Subtree solution 题解

    [题目描述] You have two every large binary trees:T1, with millions of nodes, and T2, with hundreds of no ...

  3. LeetCode题解之 Subtree of Another Tree

    1.题目描述 2.问题分析 判断一个节点,然后判断子树. 3.代码 bool isSubtree(TreeNode* s, TreeNode* t) { if (s == NULL) return f ...

  4. hdu_4918_Query on the subtree(树的分治+树状数组)

    题目链接:hdu_4918_Query on the subtree 题意: 给出一颗n个点的树,每个点有一个权值,有两种操作,一种是将某个点的权值修改为v,另一种是查询距离点u不超过d的点的权值和. ...

  5. 【LeetCode题解】二叉树的遍历

    我准备开始一个新系列[LeetCode题解],用来记录刷LeetCode题,顺便复习一下数据结构与算法. 1. 二叉树 二叉树(binary tree)是一种极为普遍的数据结构,树的每一个节点最多只有 ...

  6. “玲珑杯”ACM比赛 Round #12题解&源码

    我能说我比较傻么!就只能做一道签到题,没办法,我就先写下A题的题解&源码吧,日后补上剩余题的题解&源码吧!                                     A ...

  7. leetcode & lintcode 题解

    刷题备忘录,for bug-free 招行面试题--求无序数组最长连续序列的长度,这里连续指的是值连续--间隔为1,并不是数值的位置连续 问题: 给出一个未排序的整数数组,找出最长的连续元素序列的长度 ...

  8. LeetCode All in One题解汇总(持续更新中...)

    突然很想刷刷题,LeetCode是一个不错的选择,忽略了输入输出,更好的突出了算法,省去了不少时间. dalao们发现了任何错误,或是代码无法通过,或是有更好的解法,或是有任何疑问和建议的话,可以在对 ...

  9. LeetCode 333. Largest BST Subtree

    原题链接在这里:https://leetcode.com/problems/largest-bst-subtree/ 题目: Given a binary tree, find the largest ...

  10. NOIP2003题解

    传送门 考查题型 搜索 字符串 模拟 dp T1 神经网络 题目背景 人工神经网络(Artificial Neural Network)是一种新兴的具有自我学习能力的计算系统,在模式识别.函数逼近及贷 ...

随机推荐

  1. 加速体细胞突变检测分析流程-系列2(ctDNA等高深度样本)

    Sentieon●体细胞变异检测系列-2 Sentieon 致力于解决生物信息数据分析中的速度与准确度瓶颈,通过算法的深度优化和企业级的软件工程,大幅度提升NGS数据处理的效率.准确度和可靠性. 针对 ...

  2. 【小小demo】SpringBoot+Layui登录

    easy-login 基于layui 注册.登录简单实现,并他通过拦截器拦截未登录请求. 项目地址文章末尾 登录拦截器 SystemInterceptor preHandle在 Controller ...

  3. Delta Lake_ High-Performance ACID Table Storage over Cloud Object Stores

    论文发表于 2020年, 研究数据湖产品的很好的学习资料. 概要 开篇很明确的表明了为什么要做Delta lake这样一个产品. Databricks尝试将数据仓库直接架在云上对象存储之上, 这种尝试 ...

  4. C#选择排序(Selection Sort)算法

    选择排序原理介绍 选择排序(Selection Sort)是一种简单的排序算法,其实现原理如下: 遍历待排序数组,从第一个元素开始. 假设当前遍历的元素为最小值,将其索引保存为最小值索引(minInd ...

  5. Module build failed: TypeError: this.getOptions is not a function at Object.loader

    这个问题主要是因为node-loader版本过高导致的问题 解决方案 css-loader降为3.6.0版本即可 npm install css-loader@2.0.2 --save-dev npm ...

  6. python教程 入门学习笔记 第4天 数据类型 获取数据类型 字符串拼接

    数据类型 1.能直接处理的基本数据类型有5个:整型.浮点型.字符串.布尔值.空 1)整型(int)=整数,例如0至9,-1至-9,100,-8180等,人数.年龄.页码.门牌号等 没有小数位的数字,是 ...

  7. nflsoj 选数1 2 3

    5711 取数-1 状态表示:1维 集合:前 \(i\) 个数里面所有的选法和 属性:所有的选法和的最大值 状态计算:选或不选 选:\(f(i-1)+a_i\) 不选:\(f(i-1)\) #incl ...

  8. [刺客伍六七&黑客] 魔刀千刃

    魔刀千刃的特写 诞生之日:2023.7.29 此后会在此记录如何自己写一个自己的python库以及魔刀千刃的维护过程. 魔刀千刃(evilblade) **只攻不防,天下无双** 实战 (和堆攻击帖子 ...

  9. 文心一言 VS 讯飞星火 VS chatgpt (77)-- 算法导论7.3 2题

    二.如果用go语言,在 RANDOMIZED-QUICKSORT 的运行过程中,在最坏情况下,随机数生成器 RANDOM 被调用了多少次?在最好情况下呢?以θ符号的形式给出你的答案? 文心一言: 在 ...

  10. Three.js中实现碰撞检测

    1. 引言 碰撞检测是三维场景中常见的需求,Three.js是常用的前端三维JavaScript库,本文就如何在Three.js中进行碰撞检测进行记述 主要使用到的方法有: 射线法Raycaster ...