SRM 682 Div2 1000 SubtreesCounting

题面:定义一个树的 value 为树的所有联通子图的节点数之和
求给出的一个树的 value

感觉很巧妙的一个 dp

因为是一个无根树,不妨把0号节点提起来作为根,记 Tx 为以 x 为根节点的子树

定义 Sx 为在 Tx 中选择 x value (然后答案就是 x:Sx
定义 Cx 为在 Tx 中选择 x 的联通子图的个数

考虑两个树,分别以 st x 为根,而且分别计算出了他们各自的 S C 的值
那么,将 x 连到 st 上作为 st 的子节点时,考虑更新的 Sst 的变化,为了区别,记更新后的 Cst Sst 分别为 Croot Sroot

先考虑简单的, Croot 的值,如果不选 x ,那么方案数不变,如果选 x ,那么每一个原来的方案数都可以对应着 Cx 种方案数,合并一下就是 Cst+Cst×Cx=Cst×(Cx+1)

对于 Sroot 的值,同样考虑是否选择 x ,如果不选择,那么对 Sroot 的贡献还是 Sst ,也就是原来的方案
如果选择了 x ,情况稍微复杂一点,分别考虑 Tst Tx
对于 Tst 上的点的贡献
每一个值为 Sst 的方案都对应着 Cx 次贡献,所以对 Sroot 的贡献为 Cx×Sst
对于 Tx 上的点
同样的,每一个值为 Sx 一个方案都对应着 Cst 次贡献,所以对 Sroot 的贡献为 Cst×Sc
所以, Sroot=Sst+Cx×Sst+Cst×Sc=1+Cx×Sst+Cst×Sc


#include <bits/stdc++.h>
typedef long long LL;
using namespace std;

const int mod = 1000000007;

const int maxn = 112345;

LL dp[maxn][2];

vector<int> edge[maxn];

void Link(int st,int ed){
    edge[st].push_back(ed);
    edge[ed].push_back(st);
}

void init(int n){
    for(int i=0;i<n;i++){
        edge[i].clear();
    }
}

void dfs(int st,int fa){
    dp[st][1] = 1;
    dp[st][0] = 1;
    for(auto & x:edge[st]){
        if(x != fa){
            dfs(x,st);
            (dp[st][1] *= dp[x][0] + 1) %= mod;
            (dp[st][1] += dp[st][0] * dp[x][1]) %= mod;
            (dp[st][0] *= dp[x][0] + 1) %= mod;
        }
    }
}

class SubtreesCounting {
public:
   int sumOfSizes( int n, LL a0, LL b, LL c, LL m ) {
        init(n);
        for(int i=1;i<n;i++){
            Link(i,a0 % i);
            a0 = (b * a0 + c) % m;
        }
        dfs(0,-1);
        LL ans = 0;
        for(int i=0;i<n;i++){
            (ans += dp[i][1]) %= mod;
        }
        return (int)ans;
   }
};

你可能感兴趣的:(SRM 682 Div2 1000 SubtreesCounting)