??? note " 例题 [luogu P3806 【模板】点分治1](https://www.luogu.org/problemnew/show/P3806)"
-给定一棵有 $n$ 个点的带权树,$m$ 次询问,每次询问给出 $k$ ,询问树上距离为 $k$ 的点对是否存在。
+给定一棵有 $n$ 个点的带点权树,$m$ 次询问,每次询问给出 $k$ ,询问树上距离为 $k$ 的点对是否存在。
$n\le 10000,m\le 100,k\le 10000000$
-我们先随意选择一个结点作为根节点 $rt$ ,所有的其子树中的路径有两种情况,一种是经过这个根节点的路径,一种是不经过这个根节点的路径。对于经过根节点的路径,又有两种情况,一种是该路径的其中一个端点为根节点,另一种是两个端点都不为根节点。而第二种情况又可以由两个第一种链合并得到。所以,对于枚举的根节点 $rt$ ,我们先计算经过该节点的子树中的路径对答案的贡献,再递归子树对不经过该节点的子树中的路径进行求解。
+我们先随意选择一个节点作为根节点 $rt$ ,所有完全位于其子树中的路径可以分为两种,一种是经过当前根节点的路径,一种是不经过当前根节点的路径。对于经过当前根节点的路径,又可以分为两种,一种是以根节点为一个端点的路径,另一种是两个端点都不为根节点的路径。而后者又可以由两条属于前者链合并得到。所以,对于枚举的根节点 $rt$ ,我们先计算在其子树中且经过该节点的路径对答案的贡献,再递归其子树对不经过该节点的路径进行求解。
-在本题中,对经过根节点 $rt$ 的路径中,我们先枚举其所有子节点 $ch$ ,以 $ch$ 为根计算 $ch$ 子树中所有节点到 $rt$ 的距离。假设每个距离为 $dist_i$ ,若对一个询问的 $k$ ,满足其之前枚举到的子节点的子树中的路径(记在 $tf$ 数组中),满足 $tf[k-dist_i]$ ,则存在一条长度为 $k$ 的路径。在计算完 $ch$ 子树中所连的边能否能成为答案后,我们将这些新的距离加入 $tf$ 数组中。
+在本题中,对于经过根节点 $rt$ 的路径,我们先枚举其所有子节点 $ch$ ,以 $ch$ 为根计算 $ch$ 子树中所有节点到 $rt$ 的距离。记节点 $i$ 到当前根节点 $rt$ 的距离为 $dist_i$,$tf_{d}$ 表示之前处理过的子树中是否存在一个节点 $v$ 使得 $dist_v=d$。若一个询问的 $k$ 满足 $tf_{k-dist_i}=true$ ,则存在一条长度为 $k$ 的路径。在计算完 $ch$ 子树中所连的边能否成为答案后,我们将这些新的距离加入 $tf$ 数组中。
-注意在清空 $tf$ 数组的时候不能直接用 `memset` ,而应将之前占用过的 $tf$ 位置加入一个队列中,进行清空,保证时间复杂度。
+注意在清空 $tf$ 数组的时候不能直接用 `memset` ,而应将之前占用过的 $tf$ 位置加入一个队列中,进行清空,这样才能保证时间复杂度。
点分治过程中,每一层的所有递归过程合计对每个点处理一次,假设共递归 $h$ 层,则总时间复杂度为 $O(h\times n)$ 。
-若我们 **每次选择子树的根作为根节点** ,可以保证递归层数最少,时间复杂度为 $O(n\log_2 n)$ 。
+若我们 **每次选择子树的重心作为根节点** ,可以保证递归层数最少,时间复杂度为 $O(n\log_2 n)$ 。
-请注意在重新选择根节点之后一定要重新计算子树的大小,一点看似微小的改动可能会造成时间复杂度错误或正确性难以保证的后果。
+请注意在重新选择根节点之后一定要重新计算子树的大小,否则一点看似微小的改动就可能会使时间复杂度错误或正确性难以保证。
代码:
由于这里查询的是树上距离为 $[0,k]$ 的点对数量,所以我们用线段树来支持维护和查询。
```cpp
-// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<algorithm>
{
dist[p[j]]=w[j];
calcdist(p[j],x);
- //printf("print:");st.print(st.rt,1,20000000);printf("\n");
- //for(int k=1;k<=cnt;k++)if(q>=dd[k])ret+=tf[q-dd[k]];
for(int k=1;k<=cnt;k++)if(q-dd[k]>=0)
ret+=st.query(st.rt,1,20000000,max(0ll,1-dd[k])+1,max(0ll,q-dd[k])+1);
- //for(int k=1;k<=cnt;k++)tag.push(dd[k]),tf[dd[k]]=true;
for(int k=1;k<=cnt;k++)st.update(st.rt,1,20000000,dd[k]+1,1),tag.push(dd[k]+1);
cnt=0;
}
- //while(!tag.empty())tf[tag.front()]=false,tag.pop();
st.clear();
for(int j=h[x];j;j=nxt[j])if(p[j]!=fa&&!vis[p[j]])
{
dfz(p[j],x);
}
}
-main()
+int main()
{
scanf("%lld",&n);
for(int i=1;i<n;i++)scanf("%lld%lld%lld",&a,&b,&c),add_edge(a,b,c),add_edge(b,a,c);