洛谷 P3806 (点分治)

孤者浪人 提交于 2019-11-27 05:34:46

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

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!