浅谈虚树(虚仙人掌)

虚树是一种在OI竞赛中解决大量询问与小规模点集问题的高效方法。通过DFS序排序、求LCA并去重来构造虚树,可以实现点集基本形态的把握。文章介绍了虚树的构造方法,包括普通构造和线性构造,以及如何应用于解决如机房网络、大工程等例题。还提到了虚树在点分治和火车司机出秦川等场景的应用,强调了找到适用虚树模型的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

虚树是什么?

在 OI 比赛中,有这样一类题目:给定一棵树,另有多次询问,每个询问给定一些关键点,需要求这些关键点之间的某些信息。询问数可能很多,但满足所有询问中关键点数量的总和比较小。

由于询问数可以非常多,每次无法遍历整棵树。 我们可以用一种叫做虚树(virtual tree)的魔法来解决这一问题。

一般来说,虚树有以下一些性质: 1、虚树的大小与点集同阶 2、如果u,v两点在虚树中,则她们的LCA也在虚树中。

于是,只要把握了虚树,就把握了这个点集的基本形态。

##虚树的构造 其实很朴素

普通的构造 1、将点集按DFS序排序。 2、排序后,求相邻两点的LCA。 3、再排序一次,并去重。 这个时候,虚树上该有的点都在集合中上了。 4、建树,用一个栈来模拟。 //既然已经有了DFS序,我觉得你应该也会建树了。

int dfn[M],lim[M],stk[M],vfa[M];
//dfn:dfs序,lim:子树的dfs序的最大值,vfa:虚树上的父节点
bool cmp(int a,int b){return dfn[a]<dfn[b];}
void vbuild(int array[],int len){//对array数组构建虚树,注意array须两倍len长
	int top=0,vn;
    sort(array,array+len,cmp); //使结点按照dfs序顺序有序 
    for(int i=1;i<len;++i)
        array[++vn]=Lca(array[i-1],array[i]); //处理出相邻结点的Lca 
    sort(array,array+vn,cmp); //按dfs序再有序 
	
    stk[top++]=array[0];
    for(int i=1;i<vn;++i){
        if(array[i-1]==array[i])continue;
        while(lim[stk[top-1]]<dfn[u]) --stop; //不是祖先结点
        vfa[array[i]]=stk[stop-1];
        stk[stop++]=array[i];
    }
}

线性构造法 其实吧,排序可以用基数排序,LCA也可以O(1)求,这样就线性了。

也可以用简单一点的方法,有兴趣的同学可以去学习一下。 

##例题 好像,虚树题目都挺朴素的,不会复杂到哪里去。 要出难题的话,要么在点集的产生方式上做文章~~,要么上仙人掌~~。 ###机房网络 从这里讲起吧。 看到互质,先来一波莫比乌斯反演压压惊。 定义f(k)是权值被k整除的点集的中的点两两之间的距离之和。 定义F(k)是最大公约数为k的点集的中的点两两之间的距离之和。 显然,有

根据莫比乌斯反演定理,可以得到:

所求即为F(1)

于是,可以先筛出莫比乌斯函数,再求f(n) 考虑怎么求f(n) 直接枚举权值为n的倍数的点再建虚树就可以了。 问题转化为,求树上任意两点的距离值和。

最后一个问题,任意两点的距离和怎么求? 考虑每条边的贡献 如果有一条从u连向u的父节点的边,那么经过这条边的路径数就是

我觉得已经够清楚的了。

贴代码:


long long calc(int k){
    int tot=0,n=0,top=0;
    for(int i=k;i<M;i+=k) FOR(j,0,E[i].size()) arr[n+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值