洛谷 P3806 【模板】点分治1

This way

题意:

在这里插入图片描述

题解:

点分治模板
点分治解决的好像大部分是树上路径问题
网上模板有很多,我就不赘述了,大致思想就是找到树的重心,然后O(n)地求出路径经过这个点的所有情况,之后再将树以这个重心为基准分成多个子树,再找到他们的重心,如此分治的去做。网上的代码各不相同,我找了同集训队的人的写法,用set维护每个值是否出现,总的时间复杂度大概是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include
using namespace std;
const int N=1e4+5,M=105;
struct Edge{
    int to,next,v;
}e[N*2];
int cnt,head[N];
void add(int x,int y,int v){
    e[cnt].to=y;
    e[cnt].next=head[x];
    e[cnt].v=v;
    head[x]=cnt++;
}
int n,m,ans[M],q[M];
bool vis[N];//当前连通块是否被处理过了
int rt,mx,all,siz[N];//重心,最大子树大小,当前连通块大小,子树大小
void f_rt(int x,int fa){//找重心
    siz[x]=1;
    int mx_sub=0;
    for(int i=head[x];~i;i=e[i].next){
        int ne=e[i].to;
        if(ne==fa||vis[ne])continue;
        f_rt(ne,x);
        siz[x]+=siz[ne];
        mx_sub=max(mx_sub,siz[ne]);
    }
    mx_sub=max(mx_sub,all-siz[x]);
    if(mx_sub<mx)
        rt=x,mx=mx_sub;
}
int dis[N];
set<int>st,tmp;
void f_dis(int x,int fa,int v){
    tmp.insert(v);
    for(int i=head[x];~i;i=e[i].next){
        int ne=e[i].to;
        if(ne==fa||vis[ne])continue;
        f_dis(ne,x,v+e[i].v);
    }
}
void cal(int x){//计算当前点的答案
    st.clear();
    for(int i=head[x];~i;i=e[i].next){
        int ne=e[i].to;
        if(vis[ne])continue;
        f_dis(ne,x,e[i].v);
        for(int i=1;i<=m;i++){
            if(ans[i])continue;
            for(int j:tmp){
                if(j==q[i]||st.count(q[i]-j)){
                    ans[i]=1;
                    break;
                }
            }
        }
        for(int i:tmp)st.insert(i);
        tmp.clear();
    }
}
void divide(int x,int tot){//当前连通块和连通块大小
    cal(x);
    vis[x]=1;
    for(int i=head[x];~i;i=e[i].next){
        int ne=e[i].to;
        if(vis[ne])continue;
        all=siz[ne]>siz[x]?tot-siz[x]:siz[ne];
        rt=0,mx=all;
        f_rt(ne,0);
        divide(rt,all);
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    int x,y,v;
    for(int i=1;i<n;i++)
        scanf("%d%d%d",&x,&y,&v),add(x,y,v),add(y,x,v);
    for(int i=1;i<=m;i++)
        scanf("%d",&q[i]);
    mx=all=n;
    f_rt(1,0);
    divide(rt,n);
    for(int i=1;i<=m;i++)
        printf("%s\n",ans[i]?"AYE":"NAY");
    return 0;
}

你可能感兴趣的:(树分治)