题解:P5007 DDOSvoid 的疑惑
small_add_add · · 题解
弱鸡第一次写题解,大佬们看着玩就行orz
一、问题重述
给定一棵以
集合中任意两个节点不存在祖先-后代关系
(即任意节点不能是另一个的父节点、祖父节点等)
毒瘤指数定义为集合内所有节点的权值之和(
目标:计算所有可能毒瘤集的毒瘤指数总和,结果对
我就因为看成了1e9+7卡了好久
二、核心思路:集合合并视角的DP
传统树形DP通常定义"选/不选"状态,这里我们从集合生成角度构建新模型:
定义两个关键数组(针对以u为根的子树):
具体为什么包含空集合请看下面
核心发现:子树合并时的集合运算规律
若
此时毒瘤指数总和满足:
推导:
由于
(本质是每个集合的元素会与另一集合的所有集合组合)
第一次用LaTex写的比较丑Sorry
要包含空集的原因
这时我们就能解释为什么要包含空集了
集合合并中,空集
任何集合
如果不包含空集,合并子树时会漏掉大量合法毒瘤集。
例如:两个子树的毒瘤集分别为
if(G[u].size()==1){
sz[u]=2ll;
dp[u]=(T?u:1ll);
return;
}
2. 非叶子节点的合并
假设
Step 1:合并所有子树的毒瘤集
初始状态:
依次合并每个子树
int cnt=0;
for(int v:G[u]){
if(v==fa)continue;
Dfs(v,u);
cnt++;
if(cnt==1){
sz[u]=sz[v];
dp[u]=dp[v];
}
else{
dp[u]=dp[u]*sz[v]+dp[v]*sz[u];
sz[u]*=sz[v];
dp[u]%=mod;
sz[u]%=mod;
}
}
Step 2:加上当前点
dp[u]+=(T?u:1);
sz[u]++;
dp[u]%=mod;
sz[u]%=mod;
(因为空集的贡献为0,所以不用特殊处理)
四.代码
#include<bits/stdc++.h>
using namespace std;
using LL=long long;
LL const mod=1e8+7;
LL const N=1e6+5;
LL dp[N],sz[N];
int n,T;
vector<int>G[N];
void Dfs(int u,int fa){
if(G[u].size()==1){
sz[u]=2ll; // {},{u}
dp[u]=(T?u:1ll);
return;
}
int cnt=0;
for(int v:G[u]){
if(v==fa)continue;
Dfs(v,u);
cnt++;
if(cnt==1){
sz[u]=sz[v];
dp[u]=dp[v];
}
else{
dp[u]=dp[u]*sz[v]+dp[v]*sz[u];
sz[u]*=sz[v];
dp[u]%=mod;
sz[u]%=mod;
}
}
dp[u]+=(T?u:1);
sz[u]++;
dp[u]%=mod;
sz[u]%=mod;
}
signed main(){
cin>>n>>T;
for(int i=1;i<n;i++){
int u,v; cin>>u>>v;
G[u].emplace_back(v);
G[v].emplace_back(u);
}
Dfs(1,0);
cout<<dp[1];
return 0;
}
完结撒花