虚树是什么?
在 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+