首页 > 其他分享 >Subtree 题解

Subtree 题解

时间:2023-08-11 17:35:04浏览次数:41  
标签:suf int 题解 Subtree num include 节点 mod

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;
}

标签:suf,int,题解,Subtree,num,include,节点,mod
From: https://www.cnblogs.com/TKXZ133/p/17623551.html

相关文章

  • 国标GB28181视频云服务平台LntonGBS(源码)国标平台对接宇视SDK,多次点击录像回放出现崩溃
    LntonGBS是一款基于国标GB28181协议的视频云服务平台。通过该平台,可以实现设备接入并支持视频的实时监控直播、录像、语音对讲、云存储、告警、级联等功能。此外,LntonGBS还支持将接入的视频流进行全终端、全平台的分发,包括支持RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流分发。另......
  • 题解 LuoguP3306 [SDOI2013] 随机数生成器
    题目链接:【LuoguP3306】。前置知识OI-Wiki:快速幂,扩展欧几里得算法(exgcd),BabyStep,GiantStep算法。题意很清楚,不说。分析当\(t=x\)时答案很明显为\(1\),即在第一天就可以读到。当\(t\neqx\)时当\(a=0\)时观察一下规律:\[x_1\equivx_1\pmod{p}\]\[x_2\equivb......
  • HDU 多校 Round #6 题解
    HDU多校Round#6题解\(\text{ByDaiRuiChen007}\)A.CountProblemLink题目大意求有多少个长度为\(n\),字符集大小为\(m\)的字符串有长度为\(n-k\)的周期。数据范围:\(n,m,k\le10^{18}\)。思路分析\(k=n\)时答案为\(m^n\),否则转为有长度为\(k\)的Border,答案......
  • Royal Questions题解
    题目链接RoyalQuestions-洛谷|计算机科学教育新生态(luogu.com.cn)分析每个公主会选择两个王子,考虑将每个公主所选择的两个王子连边,边权为该公主的嫁妆选择该边即为选择该公主那么结果图是什么呢?由于每个王子最多只能选择一个公主即每个点最多有1个出边(也可能没有出边......
  • 【题解】Educational Codeforces Round 148(CF1832)
    A.NewPalindrome题目描述:给你一个由小写字母组成的回文字符串,问你是否能在重排其之后构造出另一个与原串不同的回文字符串。多测,\(t\le1000,2\le|s|\le50\)题目分析:考虑其实就是前\(\lfloor\frac{n}{2}\rfloor\)个位置存在两种或以上的不同字符,因为这样直接交换对......
  • P2203 Blink 题解
    好像并没有矩阵快速幂的题解,那我来写一篇题目分析对于每两盏灯,只考虑右灯变化,分为四种情况:左灯为\(1\),右灯为\(1\),右灯变为\(0\);左灯为\(0\),右灯为\(0\),右灯不变,为\(0\);左灯为\(1\),右灯为\(0\),右灯变为\(1\);左灯为\(0\),右灯为\(1\),右灯不变,为\(1\);设第\(i\)......
  • P9342 Bitaro's Travel 题解
    模拟赛做到的题,赛后看了Y2hlbnlpa2Fp的题解,感觉没讲清楚,这里做下补充,提供自己的理解。基本思路:对每个\(A_i\)的答案进行预处理,对于每个询问,只需要找到第一个到达的景点即可。那么如何预处理每个点的答案呢?有一条很重要的性质:最多转向\(\log{X}\)次。要证明这个结论,先放......
  • P6879 スタンプラリー 3 题解
    思路前几篇题解都介绍了,这里着重介绍一个状态设计的小技巧。在设计状态时,我们可能会碰到状态数值过大,而dp数组内存的值较小的情况。例如在该题用\(dp_{l,r,t,0/1}\)表示逆时针经过\(l\)个,顺时针经过\(r\)个,已经花费\(t\)秒,所拿到的雕像个数,\(0\)表示当前在左端点,\(1\)......
  • AT_apc001_g Colorful Doors 题解
    模拟赛做到的题,场上写贪心爆栈了qwq首先在首尾加上两个\(1\)表示进出,将两段路中间的间隔作为传送门,恰好有\(2\timesN\)个传送门,根据两段路的经过情况给传送门分类别:00:用\(N\)表示,称为无用点,不到达该点。10:用\(S\)表示,称为起点,需要通过向右走走到一次。01:用\(T\)......
  • 关于Promise的超难面试题解读
    让我来看一下题目,如下所示Promise.resolve().then(()=>{ console.log(0); returnPromise.resolve(4); }).then((res)=>{ console.log(res); }); Promise.resolve().then(()=>{ console.log(1); }).then(()=>{ console.log(2); }).t......