这是个点分治模板题。
那么点分治是干什么用的呢?
当然就是解决这个问题的啦!
我们现在分析一下这个题目怎么解决。
询问有没有树上点对距离为k的点对,那么我们显然可以从每一个结点开始dfs,然后查看是否有距离为k的点对就可以了。但是,这个显然是不能解决这个问题的。因为复杂度很大。
我们来分析一下,对于树上的点对问题,可以归结为两类,
- 点对的路径经过某一个根节点
- 点对在某一棵树的子树中
那么我们就可以分治进行处理了。
首先处理一个根,然后处理其子树
如果我们的根选的’恰当’的话,这个问题的复杂度就会大大降低。
如果我们每次选的根都是当前树的重心,那么树的层数就会变成大致log级别,复杂度就会降到log级别了。
现在我们依次处理点分治的算法流程:
- 首先求树的重心(重心的定义是使树的左右子树的数目最大值最小的结点),我们可以使用dp来求重心,这个比较简单,下面会在代码中解释。
- 对于每一个重心,求出其子树的结点到重心的距离。这个直接dfs就可以求出来。
- 重心确定好了,距离求出来了,我们就可以统计答案了,统计答案不细说了,具体问题具体分析吧。
- 然后我们递归处理重心的子树。
下面放上代码,这次的代码里有了不少注释,相信大家可以看的懂的。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+7;
int sz[maxn],f[maxn],dis[maxn]; //分别表示树的大小,树的最大值,距离
int vis[maxn];
int ans[100000010]; //离线记录答案
int cnt = 0;
int sum = 0,rt = 0;
int n,m;
struct node
{
int to;
int cost;
};
vector<node> G[maxn];
void getrt(int now,int fa) //求重心,选根
{
sz[now] = 1;
f[now] = 0;
for(int i=0;i<G[now].size();i++)
{
int v = G[now][i].to;
if(v!=fa && !vis[v])
{
getrt(v,now);
sz[now] += sz[v];
f[now] = max(f[now],sz[v]); //找子树中的最大值
}
}
f[now] = max(f[now],sum-sz[now]); //一个结点可以把图分成上下两部分,我们更新一下上下两部分的最大值
if(f[now]<f[rt]) rt = now; //我们要选的是最大值最小的,所以把根节点更新
}
void getdis(int now,int fa,int len) //求结点到根节点的距离
{
dis[++cnt] = len;
for(int i=0;i<G[now].size();i++)
{
int v = G[now][i].to;
if(v!=fa && !vis[v])
{
getdis(v,now,len+G[now][i].cost);
}
}
}
void solve(int now,int len,int w) //记录答案
{
cnt = 0;
getdis(now,0,len);
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=cnt;j++)
{
if(i!=j) ans[dis[i]+dis[j]] += w;
}
}
}
void divide(int now) //点分治
{
vis[now] = 1; //标记访问过now
solve(now,0,1);//加上个数
for(int i=0;i<G[now].size();i++)
{
int v = G[now][i].to;
if(!vis[v])
{
solve(v,G[now][i].cost,-1); //减掉重复的个数,因为solve(now,0,1)会被计算两次
rt = 0; //下面就是重新找重心,选根结点
sum = sz[now];
f[rt] = n;
getrt(v,now);
divide(rt); //处理子树
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n-1;i++)
{
int x,z,y;
scanf("%d%d%d",&x,&y,&z);
G[x].push_back({y,z});
G[y].push_back({x,z});
}
sum = n;
f[rt=0] = n;
getrt(1,0);
divide(rt);
for(int i=0;i<m;i++)
{
int k;
scanf("%d",&k);
if(ans[k]) printf("AYE\n");
else printf("NAY\n");
}
return 0;
}
如果看不懂的话可以留言或者发邮件(cncaoyue@nuist.edu.cn)或者加Q(304960005)(加备注),我会尽力解答大家的疑惑。