题目:https://www.luogu.org/problem/P3806
题意:一棵树,下面有q个询问,问是否有距离为k的点对
思路:牵扯到树上路径的题都是一般都是点分治,我们可以算出所有的路径长度然后保留下来,点分治无非就是几步一直递归,点分治就是在树上递归
1,找树的重心
2,算出所有点到重心距离,找出当前重心的所有合法路径
3,递归到子树
然后反复执行这三步
其实点分治唯一思考的地方就是 solve函数,其他都是一样的
https://www.cnblogs.com/guoshaoyang/p/10994997.html
https://www.cnblogs.com/PinkRabbit/p/8593080.html
#include<bits/stdc++.h>
#define maxn 100005
#define mod 1000000007
using namespace std;
typedef long long ll;
vector<pair<ll,ll> > mp[maxn];//存下图
bool vis[maxn];//标记曾经使用过的重心
ll maxsize[maxn],dis[maxn],d[maxn];//maxsize 当前节点的最大子树
ll siz[maxn],e[maxn];// dis 到重心的距离 d 出现过的距离
ll n,m,rt,sum,qe; // siz 当前节点的子树个数 e 出现的距离 rt代表当前重心
void find(ll x,ll f){//找出重心
siz[x]=1;
maxsize[x]=0;
for(int i=0;i<mp[x].size();i++){
pair<ll,ll> q=mp[x][i];
if(q.first==f||vis[q.first]) continue;//vis数组标记曾经使用过的重心
find(q.first,x);
siz[x]+=siz[q.first];
maxsize[x]=max(maxsize[x],siz[q.first]);
}
maxsize[x]=max(maxsize[x],sum-siz[x]);//节点总数减去当前的子树数=以当前节点为根的父亲点子树数
if(maxsize[x]<maxsize[rt]){
rt=x;
}
}
void get_dis(ll x,ll f,ll len){//得到每个点到重心的距离
if(len<=1e7){
e[++qe]=len;
}
for(int i=0;i<mp[x].size();i++){
pair<ll,ll> q=mp[x][i];
if(q.first==f||vis[q.first]) continue;
dis[q.first]=dis[x]+len;
get_dis(q.first,x,len+q.second);
}
}
void solve(ll x,ll len,ll w){
qe=0;
dis[x]=len;
get_dis(x,0,len);
for(int i=1;i<=qe;i++){
for(int j=1;j<=qe;j++){
if(i!=j){
d[e[i]+e[j]]+=w;
}
}
}
}
void divide(ll x){//循环执行三步
solve(x,0,1);
vis[x]=1;
for(int i=0;i<mp[x].size();i++){
pair<ll,ll> q=mp[x][i];
if(vis[q.first]) continue;
solve(q.first,q.second,-1);
sum=siz[q.first];
rt=0;
maxsize[rt]=mod;
find(q.first,x);
divide(rt);
}
}
int main(){
cin>>n>>m;
ll a,b,c;
for(int i=0;i<n-1;i++){
cin>>a>>b>>c;
mp[a].push_back(make_pair(b,c));
mp[b].push_back(make_pair(a,c));
}
sum=n;//当前节点数
rt=0;
maxsize[0]=mod;//置初值
find(1,0);
divide(rt);
while(m--){
cin>>a;
if(d[a]){
printf("AYE\n");
}
else{
printf("NAY\n");
}
}
}