您的位置:首页 > 其它

SRM 682 Div2 1000 SubtreesCounting

2016-03-03 18:35 369 查看
题面:定义一个树的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;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: