CF1770E Koxia and Tree

文章介绍了如何解决一个涉及无根树的数学问题,其中部分节点带有标记。首先,计算不考虑标记转移时距离的期望值,可以通过DFS遍历树并维护每个子树的标记节点数量来实现。然后,考虑标记在边上的转移情况,分析不同情况下边对期望值的贡献,通过分类讨论计算每条边更新后的概率,最终得到答案。整个过程的时间复杂度为O(n)。

CF1770E Koxia and Tree

题目大意

有一棵 n n n个结点的无根树,边从 1 1 1 n − 1 n-1 n1标号,第 i i i条边连接结点 u i u_i ui v i v_i vi

k k k个结点带有标记,分别是 a 1 , a 2 , … a k ( ∀ i ≠ j , a i ≠ a j ) a_1,a_2,\dots a_k(\forall i\neq j,a_i\neq a_j) a1,a2,ak(i=j,ai=aj)。接下来,进行如下操作:

  • 给每条边随机定一个方向
  • 按照编号的顺序,对于每条边 u → v u\rightarrow v uv,如果结点 u u u有标记而结点 v v v没有,那么就把结点 u u u的标记转移到结点 v v v
  • 随机选取两个不同的标记点,计算它们的距离。

请你计算第三步中距离的期望值。对 998244353 998244353 998244353取模。


题解

我们先考虑如果不转移标记,距离的期望值为多少。

此时距离的期望值就是任意两点距离的总和减去任意选择两个点的方案数,那么只需求出任意两点距离的总和即可。我们可以分别计算每条边的贡献。因为路径上有这条边的两个点一定是一个在左一个在右,所以一条边的贡献为其左边的结点中有标记的点的个数与右边的结点中有标记的点的个数之积。令一个点为根,维护每个结点所在子树的有标记的点的个数,即可用dfs来 O ( n ) O(n) O(n)求出答案。

在这里插入图片描述
我们再考虑会转移标记的情况下的距离期望值。

首先我们知道,在遍历一条边之前,没有标记能跨越这条边,所以这条边两边的有标记的点是不变的。对于一条边 ( u , v ) (u,v) (u,v),假设 u u u v v v的父亲,那么 u u u这边有标记的点的个数为 k − s v k-s_v ksv v v v这边有标记的点的个数 s v s_v sv

u u u点有标记的概率为 f u f_u fu v v v点有标记的概率为 f v f_v fv。我们可以分类讨论。

  • 两端都有点,则贡献为 f u × f v × ( k − s v ) × s v f_u\times f_v\times (k-s_v)\times s_v fu×fv×(ksv)×sv
  • u u u有点, v v v没有点,如果方向为 u → v u\rightarrow v uv,则 u u u点的标记转移到 v v v,否则不转移,概率各占 1 2 \dfrac 12 21,贡献为
    ( 1 − f u ) × f v × ( k − s v − 1 ) × ( s v + 1 ) + ( k − s v ) × s v 2 \qquad \qquad (1-f_u)\times f_v\times\dfrac{(k-s_v-1)\times (s_v+1)+(k-s_v)\times s_v}{2} (1fu)×fv×2(ksv1)×(sv+1)+(ksv)×sv
  • u u u没有点, v v v有点,如果方向为 v → u v\rightarrow u vu,则 v v v点的标记转移到 u u u,否则不转移,概率各占 1 2 \dfrac 12 21,贡献为
    f u × ( 1 − f v ) × ( k − s v + 1 ) × ( s v − 1 ) + ( k − s v ) × s v 2 \qquad \qquad f_u\times (1-f_v)\times\dfrac{(k-s_v+1)\times (s_v-1)+(k-s_v)\times s_v}{2} fu×(1fv)×2(ksv+1)×(sv1)+(ksv)×sv
  • 两端都没有点,贡献为 ( 1 − f u ) × ( 1 − f v ) × ( k − s v ) × s v (1-f_u)\times (1-f_v)\times (k-s_v)\times s_v (1fu)×(1fv)×(ksv)×sv

这四个式子之和就是这条边对答案的贡献,累加即可。

那么遍历这条边后,两边有点的概率会变为多少呢?

  • 两边都有点,遍历这条边后 u u u有点和 v v v有点的概率为 f u × f v f_u\times f_v fu×fv
  • u u u有点, v v v没有点,遍历这条边后,两种方向的概率各占 1 2 \dfrac 12 21 u u u有点和 v v v有点的概率为 f u × ( 1 − f v ) 2 \dfrac{f_u\times (1-f_v)}{2} 2fu×(1fv)
  • v v v有点, u u u没有点,遍历这条边后,两种方向的概率各占 1 2 \dfrac 12 21 u u u有点和 v v v有点的概率为 ( 1 − f u ) × f v 2 \dfrac{(1-f_u)\times f_v}{2} 2(1fu)×fv
  • 两边没有点,遍历这条边后 u u u有点和 v v v有点的概率为 ( 1 − f u ) × ( 1 − f v ) (1-f_u)\times (1-f_v) (1fu)×(1fv)

那么遍历这条边后 f u = f v = f u + f v 2 f_u=f_v=\dfrac{f_u+f_v}{2} fu=fv=2fu+fv

最后的答案要乘上方案数的逆元。

时间复杂度为 O ( n ) O(n) O(n)

code

#include<bits/stdc++.h>
using namespace std;
int n,x,y,tot=1,fa[300005],d[600005],l[600005],r[600005];
long long k,ans=0,siz[300005],p[300005];
long long ny2=499122177,mod=998244353;
void add(int xx,int yy){
	l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
void dfs(int u,int f){
	fa[u]=f;siz[u]=p[u];
	for(int i=r[u];i;i=l[i]){
		if(d[i]==f) continue;
		dfs(d[i],u);
		siz[u]+=siz[d[i]];
	}
}
long long mi(long long t,long long v){
	if(!v) return 1;
	long long re=mi(t,v/2);
	re=re*re%mod;
	if(v&1) re=re*t%mod;
	return re;
}
int main()
{
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=k;i++){
		scanf("%d",&x);p[x]=1;
	}
	for(int i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	dfs(1,0);
	for(int i=2;i<=tot;i+=2){
		int u=d[i^1],v=d[i];
		if(fa[u]==v) swap(u,v);
		ans=(ans+p[u]*p[v]%mod*(k-siz[v])%mod*siz[v]%mod
			+(mod+1-p[u])*(mod+1-p[v])%mod*(k-siz[v])%mod*siz[v]%mod
			+p[u]*(mod+1-p[v])%mod*((k-siz[v])*siz[v]%mod+(k-siz[v]-1)*(siz[v]+1)%mod)%mod*ny2%mod
			+(mod+1-p[u])*p[v]%mod*((k-siz[v])*siz[v]%mod+(k-siz[v]+1)*(siz[v]-1)%mod)%mod*ny2%mod)%mod;
		p[u]=p[v]=(p[u]+p[v])*ny2%mod;
	}
	printf("%lld",ans*mi(k*(k-1)/2%mod,mod-2)%mod);
	return 0;
}
04-24
<think>好的,我现在需要帮助用户了解CF树的相关信息和使用方法。首先,我应该回忆一下CF树的基本概念。CF树是BIRCH聚类算法中的核心数据结构,用于高效处理大规模数据。用户可能已经知道聚类的基本概念,但需要更深入的技术细节。 接下来,我需要确认CF树的组成部分。每个节点由聚类特征(CF)组成,而CF包含三个部分:数据点的数量(N)、线性和(LS)和平方和(SS)。这些统计量帮助快速计算聚类的基本信息,比如质心和半径。需要解释清楚这些术语,并用数学表达式展示,比如LS和SS的公式。 然后,结构方面,CF树是一个平衡树,每个节点有多个子节点,不超过阈值B;叶子节点则不超过阈值L。分裂过程类似于B树,当插入新数据导致节点超过阈值时,会进行分裂。这里可能需要举例说明分裂的过程,比如如何选择最近的子节点,如何合并或分裂节点。 关于参数,用户可能需要知道如何调整B和L来平衡精度和效率。较小的阈值会导致树更深,可能影响效率;较大的阈值可能降低聚类质量。这部分需要结合实际应用场景的建议,比如处理大规模数据时的调参策略。 使用场景方面,CF树适合流式数据或内存有限的情况,因为它可以增量更新,不需要重新计算全部数据。但也要指出其局限性,比如对高维数据效果可能下降,或者球形簇的假设。这能帮助用户判断是否适合他们的需求。 最后,生成相关问题时,需要覆盖结构、参数调优、优缺点、应用场景以及与其他算法的对比。这些问题能引导用户进一步探索CF树的不同方面。同时,确保引用正确的文献,比如用户提供的引用[1]中的聚类特征定义,以及引用[3]中的聚类综述,以增强回答的可信度。 检查是否有遗漏的部分,比如是否解释了CF的三个分量如何计算质心和半径,是否有示例说明插入过程,是否提到了BIRCH算法的步骤。确保数学公式使用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$。同时,回答结构要清晰,分点说明,帮助用户逐步理解。</think>### CF树(聚类特征树)详解 CF树(Clustering Feature Tree)是层次聚类算法BIRCH的核心数据结构,专为处理大规模数据设计。其核心思想是通过紧凑的聚类特征(CF)逐步构建树形结构,实现高效的数据压缩和聚类分析[^1]。 #### 1. 聚类特征(CF)定义 每个CF包含三个关键统计量: $$ CF = (n, LS, SS) $$ - $n$:子簇中数据点数量 - $LS = \sum_{i=1}^n x_i$(线性和) - $SS = \sum_{i=1}^n x_i^2$(平方和) 通过这些统计量可快速计算: - 质心:$\frac{LS}{n}$ - 簇半径:$\sqrt{\frac{SS}{n} - (\frac{LS}{n})^2}$ #### 2. CF树结构特性 - **平衡树结构**:类似B+树,包含根节点、内部节点和叶节点 - **节点容量限制**: - 内部节点最大分支数:$B$(分支因子) - 叶节点最大CF数:$L$(叶容量) - 簇直径阈值:$T$(控制簇紧密程度) #### 3. 构建过程示例 当插入新数据点$x$时: 1. 从根节点向下寻找最接近的CF路径 2. 到达叶节点后: - 若合并后簇直径$\leq T$:吸收到现有CF - 否则:创建新CF条目 3. 若节点溢出则分裂,分裂策略类似B树 ```python # 伪代码示例 class CFNode: def insert(self, point): if self.is_leaf: candidate = find_closest_cf(point) if merged_diameter(candidate, point) <= T: candidate.update(point) else: create_new_cf(point) if len(CFs) > L: self.split() else: route_to_child(point) ``` #### 4. 关键参数调优 | 参数 | 影响 | 建议值 | |------|------|-------| | $B$ | 树宽度 | 50-200 | | $L$ | 叶节点容量 | 20-100 | | $T$ | 簇紧密度 | 根据数据分布调整 | #### 5. 应用场景 - **流式数据处理**:支持增量更新[^1] - **内存受限环境**:典型内存消耗仅为原始数据的1/10 - **数据预处理**:可生成初步聚类作为其他算法的输入 #### 6. 优缺点分析 ✓ 优点: - 时间复杂度$O(n)$ - 可处理任意形状数据 - 自动处理噪声点 ✗ 局限性: - 对高维数据效果下降 - 依赖数据输入顺序 - 需要合理设置阈值参数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值