[模板]点分治

link

试题分析

其实就是一个十分暴力的算法,每次找到树的重心以后把它当为根然后再重新进行

这道题所到达长度为$k$的道路有$2$种情况

一种情况是经过根$root$,$x$到$y$的距离为$dis[x]+dis[y](lca(x,y)=root)$

而还有一种是经过子树的,就去进行分治即可
然后就没有了

如果是暴力方法的时间复杂度为:$O(floor \times n)$ $floor$为树的层数

如果是点分治的话,因为每次是树的重心所以层数不超过$\log n$,所以时间复杂度为:$O(n\log n)$

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=10001;
const int MAXN1=10000001;
int n,q;
struct node{
    int u,v,w,nex;
}x[MAXN<<1];
int cnt,head[MAXN],vis[MAXN],root,sum[MAXN],maxnson[MAXN],sigma,maxn=INT_MAX,query[MAXN],rem[MAXN];
void add(int u,int v,int w){
    x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].nex=head[u],head[u]=cnt++;
}
void get(int xx,int fath){
    sum[xx]=1;
    for(int i=head[xx];i!=-1;i=x[i].nex){
        if(x[i].v==fath) continue;
        if(vis[x[i].v]) continue;
        get(x[i].v,xx);
        maxnson[xx]=max(maxnson[xx],maxnson[x[i].v]);
        sum[xx]+=sum[x[i].v];
    }
    int pd=max(maxnson[xx],sigma-sum[xx]);
    if(pd<maxn){
        pd=maxn;
        root=xx;
    }
    return;
}
int num_dis[MAXN1],dis[MAXN];
void dfs(int xx,int fath){
    rem[++rem[0]]=dis[xx];
    for(int i=head[xx];i!=-1;i=x[i].nex){
        if(x[i].v==fath) continue;
        if(vis[x[i].v]) continue;
        dis[x[i].v]=dis[xx]+x[i].w;
        dfs(x[i].v,xx);
    }return;
}
bool pd[MAXN];
int que[MAXN];
void calc(int xx,int fath){
    que[0]=0;
    for(int i=head[xx];i!=-1;i=x[i].nex){
        if(vis[x[i].v]) continue;
        if(x[i].v==fath) continue;
        rem[0]=0;
        dis[x[i].v]=x[i].w;
        dfs(x[i].v,xx);
        for(int j=1;j<=q;j++)
            for(int z=1;z<=rem[0];z++) 
                if(query[j]>=rem[z]) pd[j]|=num_dis[query[j]-rem[z]];
        
        for(int j=1;j<=rem[0];j++) que[++que[0]]=rem[j],num_dis[rem[j]]=1;    
    }
    for(int i=1;i<=que[0];i++) num_dis[que[i]]=0;
}
void solve(int xx,int fath){ 
    vis[xx]=1,num_dis[0]=1;calc(xx,fath);
    for(int i=head[xx];i!=-1;i=x[i].nex){
        if(x[i].v==fath) continue;
        if(vis[x[i].v]) continue;
        sigma--;
        get(x[i].v,xx);        
        solve(x[i].v,xx);
    }
}
int main(){
    memset(head,-1,sizeof(head));
    n=read(),q=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w);
    }
    for(int i=1;i<=q;i++) query[i]=read();
    sigma=n,get(1,0);
    get(1,0);
    solve(1,0);
    for(int i=1;i<=q;i++){
        if(pd[i]) printf("AYE\n");
        else printf("NAY\n");
    }
}
View Code

 

转载于:https://www.cnblogs.com/si-rui-yang/p/10048592.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值