题目: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"); } } }