@rebirth1120
2019-07-04T09:56:44.000000Z
字数 1149
阅读 804
OI知识
树
参考资料:洛谷日报2018,8 #23 [守望] 点分治略解
处理树上有关路径的问题,如:求一棵树上长度为 k 的路径有多少条。
分治 (毫无疑问)。
对于树上的每一个点,一条路径有两种情况
1. 经过这个点
2. 在这个点的子树上
那么对于情况 1,我们可以 dfs 算出它所有子树上的节点到它的距离,从而求出长度为 k 的路径数量;
而对于情况 2,利用分治的思想,我们可以对当前节点的每一棵子树进行对情况 1 的处理,这样递归下去,最终就可以求得答案。
void get_rt(int u,int f){
int maxn=0;size[u]=1; // 这里 size 必须先初始化为 1
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(v==fa||vis[v]) continue; // 由于要多次找重心,所以要避开已经扫过的点
get_rt(v,u);
size[u]+=size[v];
maxn=max(maxn,size[v]);
}
maxn=max(maxn,num-size[u]);
if(maxn<maxp){
maxp=maxn;
rt=u;
}
void get_dis(int u,int fa,int dis){
if(dis>k) return;
bot[++bot[0]]=dis; // 先存起来,以便等下统计
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(v==fa||vis[v]) continue;
get_dis(v,u,dis+len[i]);
}
}
void calc(int u){
t=0;
tms[0]=1; // 到节点 u 不同长度的点出现的次数
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(vis[v])continue;
bot[0]=0; // 初始化,清零
get_dis(v,u,len[i],1);
for(int j=1;j<=bot[0];j++)
ans+=tms[k-bot[j]]; // 统计
for(int j=1;j<=bot[0];j++){
q[++t]=b1[j];
tms[bot[j]]++; // 统计完一棵子树后再把它存进去,防止统计到同一子树中的节点
}
}
for(int i=1;i<=t;i++)
tms[q[i]]=0; // 由于 k 可能比较大,用 memset 有可能 TLE,所以用一个队列存修改了的点,最后再一一清零
}