牛客国庆集训派对Day3 B-Tree (树上包含某个节点的连通子集个数)

链接:https://www.nowcoder.com/acm/contest/203/B
来源:牛客网
 

题目描述

修修去年种下了一棵树,现在它已经有n个结点了。
修修非常擅长数数,他很快就数出了包含每个点的连通点集的数量。
澜澜也想知道答案,但他不会数数,于是他把问题交给了你。

输入描述:

第一行一个整数n (1≤ n ≤ 106),接下来n-1行每行两个整数ai,bi表示一条边 (1≤ ai,bi≤ n)。

输出描述:

输出n行,每行一个非负整数。第i行表示包含第i个点的连通点集的数量对109+7取模的结果。

示例1

输入

复制

6
1 2
1 3
2 4
4 5
4 6

输出

复制

12
15
7
16
9
9

 

解题思路:对于一棵树,不妨设以1为根。那么我们可以通过一个树上dp,很容易的就能求出包含根的树上连通点集个数。方程如下。这样子就计算出了以u为根的子树中包含u的连通点集个数。

dp[u]=dp[u]*(dp[v]+1);

但是这样子只有根的答案是正确的,其他节点的答案都不正确。

假设我们已经知道了一个节点往上走的子树的答案ans,那么我们就可以很容易的计算出当前子树的正确答案

dp[u]=(ans+1)*dp[u];

那么我们怎么算出网上走的子树的答案呢?我们在深搜的时候,暴力计算其他孩子对答案的贡献即可,但是这样复杂度最坏是O(N*sqrt(N))的,所以要优化,实际上对于往上走的答案,用它父亲的答案除以dp[u]+1即可算出,但是这里要用逆元处理,会出现inv(MOD)%MOD==0的情况,这里用暴力算就好了。

 

#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include<string>
#include<vector>
#include<bitset>
using namespace std;
typedef long long ll;
const int MAXN=1000006;
const ll MOD=1e9+7;
 
ll pow_mod(ll a, ll k) {
    ll rst = 1;
    while (k) {
        if (k&1) rst = rst * a % MOD;
        a = a * a % MOD;
        k >>= 1;
    }
    return rst;
}
inline ll inv(ll x) {
    return pow_mod(x, MOD - 2)%MOD;
}
 
inline void scan_d(int &ret)
{
    char c;
    ret = 0;
    while ((c = getchar()) < '0' || c > '9');
    while (c >= '0' && c <= '9')
    {
        ret = ret * 10 + (c - '0'), c = getchar();
    }
}
 
 
void Out(ll a)
{   //  输出外挂
    if (a < 0)
    {
        putchar('-');
        a = -a;
    }
    if (a >= 10)
    {
       Out(a / 10);
    }
    putchar(a % 10 + '0');
}
 
 
vector<int> G[MAXN];
ll dp[MAXN];//保存所有子树的的包含子树的根的连通点集个数
int pre[MAXN];
ll sta[MAXN];//保存不包含当前子树的包含子树的根的父亲的连通点集个数。
ll ans[MAXN];
int N;
void dfs1(int u,int fa){
    pre[u]=fa;
    for(int i=0;i<G[u].size();i++){
        if(G[u][i]!=fa){
            dfs1(G[u][i],u);
            dp[u]=dp[u]*(dp[G[u][i]]+1)%MOD;
        }
    }
}
 
void dfs2(int u,int fa){
 
    if(fa!=-1){
        if((dp[u]+1)%MOD==0)//特殊情况,暴力处理
        {
            ll num=sta[fa]+1;
            for(int i=0;i<G[fa].size();i++){
                int v=G[fa][i];
                if(v==pre[fa]||v==u)
                    continue;
                num=num*(dp[v]+1)%MOD;
            }
            sta[u]=num;
            ans[u]=(num+1)*dp[u]%MOD;
        }
        else//否则用逆元直接计算。
        {
            ll num=ans[fa]*inv(dp[u]+1)%MOD;
            sta[u]=num;
            ans[u]=(num+1)*dp[u]%MOD;
        }
    }
 
    for(int i=0;i<G[u].size();i++){
        if(G[u][i]!=fa){
            dfs2(G[u][i],u);
        }
    }
}
 
 
int main(){
 
    scan_d(N);
    int u,v;
 
    for(int i=1;i<N;i++){
        dp[i]=1;
        scan_d(u);
        scan_d(v);
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }
    dp[N]=1;
    dfs1(1,-1);
    ans[1]=dp[1];
    dfs2(1,-1);
 
    for(int i=1;i<=N;i++){
       Out(ans[i]);
       puts("");
    }
 
    return 0;
}

 

 

 

 

 

    原文作者:B树
    原文地址: https://blog.csdn.net/lzc504603913/article/details/82933072
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞