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. SQL Sever 基础语法(增)

    SQL Sever  插入(Insert)基础语法详解 在SQL中,向表中插入数据是最基础的,任何对数据处理的基础就是数据库有数据,对于SQL而言,向表中插入数据有多种方法,本文列举3种: (一) 标 ...

  2. 彻底解决各种浏览器访问不了GitHub问题(注意代理)

    如果有穿墙插件如Google助手  VPN  SS 之类别 有可能被全局代理 首先关闭这些软件 或者浏览器插件 假设,您的本地代理端口为:1080 ,打开git base窗口进行按下列的方式设置.(在 ...

  3. Linux网络设备命名规则简介

    Linux网络设备命名规则简介 几年前, Linux内核为网络接口分配名称采用的是一种简单和直观的方式:一个固定的前缀和一个递增的序号.比如,内核使用eth0名称以标识启动后第一个加载的网络设备,第二 ...

  4. JAVA代码下载TXT文件(本地和服务器上的代码都可以)

    // 读取服务器文件内容(TXT文件测试可以) public static List<String> showTxt(String filePath) throws IOException ...

  5. Semantic Kernel(语义内核)秋季路线图

    Semantic Kernel 是一个开源的 SDK,它允许开发人员将大型语言模型(LLM)与传统的编程语言进行混合使用. 微软Semantic Kernel团队 在博客上正式公布了Semantic ...

  6. C补充

    C语言之unsigned 与signed 无符号与有符号数据的操作区别在于当最高位,当最高位为0时都一样: 在C中,默认的基础数据类型均为signed,现在我们以char为例,说明(signed) c ...

  7. 文心一言 VS 讯飞星火 VS chatgpt (66)-- 算法导论6.5 5题

    五.试分析在使用下列循环不变量时,HEAP-INCREASE-KEY 的正确性:在算法的第4~6行 while循环每次迭代开始的时候,子数组 A[1..A.heap-size]要满足最大堆的性质.如果 ...

  8. k8s实战案例之运行WordPress

    1.WordPress架构 LNMP案例之基于Nginx+PHP实现WordPress博客站点,要求Nginx+PHP运⾏在同⼀个Pod的不同容器;nginx主要作用是接入站点请求,如果请求静态资源n ...

  9. ATtiny88初体验(二):呼吸灯

    ATtiny88初体验(二):呼吸灯 前面的"点灯"实验实现了间隔点亮/熄灭LED,但是间隔时间和亮度都没法控制,为了解决这个问题,可以使用ATtiny88的定时器模块. ATti ...

  10. 记录一个令人崩溃的tomcat闪退问题

    tomcat启动时要加载server.xml文件,xml文件中的注释符要一一对应不能多不能少. 比如 这就是错的 只有这样 才是对的 呜呜呜~~~