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. 深入浅出synchronized的原理与源码

    深入浅出synchronized的原理与源码 1.java对象头关于锁的标识 1.对象头 // 32 bits: // -------- // hash:25 ------------>| ag ...

  2. Seal AppManager v0.2 发布:进一步简化应用部署体验

    经过近3个月的研发,Seal AppManager v0.2 已正式发布. Seal AppManager 是一款基于平台工程理念的应用统一部署管理平台,于今年4月首次推出.在上一版本中,我们已经释出 ...

  3. 【题解】ABC293E Sol

    题目大意 给定整数 \(A,X,M\),求 \(\sum\limits^{X-1}_{i=0} A^i\) 对 \(M\) 取模的值. 数据范围:\(1 \le A,M \le 10^9\),\(1 ...

  4. 现代C++(Modern C++)基本用法实践:四、模板

    概述 C++的模板是泛型编程思想的一种实现.C++是强类型语言,处处强调类型.同样的加法运算,int和float的加法运算需定义两个函数(重载),而使用模板则可以只用一个函数(见下面示例). 这类似我 ...

  5. Llama2开源大模型的新篇章以及在阿里云的实践

    Llama一直被誉为AI社区中最强大的开源大模型.然而,由于开源协议的限制,它一直不能被免费用于商业用途.然而,这一切在7月19日发生了改变,当Meta终于发布了大家期待已久的免费商用版本Llama2 ...

  6. YUM histoy 与 RPM -qa --last

    查看Linux yum安装包的安装时间,可以使用以下命令: rpm -qa --last 该命令将显示已安装的所有rpm包及其安装日期和时间. 可以使用管道符 '|' 和 grep 命令来查找特定的包 ...

  7. 分布式ID性能评测:CosId VS 美团 Leaf

    分布式ID性能评测:CosId VS 美团 Leaf 基准测试环境 MacBook Pro (M1) JDK 17 JMH 1.36 运行在本机的Docker 的 mariadb:10.6.4 运行基 ...

  8. Cannot use v-for on stateful component root element because it renders multiple elements.

    <template name:trailerStars> <image v-for="yellow in yellowScore" src="../st ...

  9. 记一次线上问题 → Deadlock 的分析与优化

    开心一刻 今天女朋友很生气 女朋友:我发现你们男的,都挺单纯的 我:这话怎么说 女朋友:脑袋里就只想三件事,搞钱,跟谁喝点,还有这娘们真好看 我:你错了,其实我们男人吧,每天只合计一件事 女朋友:啥事 ...

  10. 双URL编码绕过WAF

    一般编码一次是%5c. 但攻击者怕这个会被认出来,所以用二次编码,把%本身编码成%25.再和后边拼成%255c. 如果URL解码器有缺陷,只不断重复"从前边开始解析"这个步骤,就会 ...