boatherds题解(oiclass1464)

/*点分治模板,思路:

两个点的路径(可能不是最短的),必定能经过两点所在子树的根

于是我们枚举子树,并求出重心,以重心为根(优化)

用d[u]表示u点到子树根(root)的距离,ans[u]表示距离为u的两个点有多少组

则d[i]+d[j]表示i到j的距离(经过root,可能不是最短)

所以++ans[d[i]+d[j]]

但是,d[i]+d[j]不一定是i到j的最短路长度

d[i]+d[j]-i到j的距离=root到lca(i,j)*2

所以--ans[d[i]+d[j]-w](具体见代码)

*/

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 10010
#include<vector>
#define M 10000001
using namespace std;
int n,dis[N],mn,size[N],root,ans[M],cnt,m,d[N],f[N];
vector< int > son[N],t[N];
void getroot( int u, int fa){ //求重心,与黑手党类似
     ++size[u];
     int mx=0;
     for ( int i=0;i<son[u].size();++i){
         int v=son[u][i],w=t[u][i];
         if (v!=fa&&!f[v]){
             getroot(v,u);
             size[u]+=size[v];
             mx=max(mx,size[v]);
         }
     }
     mx=max(mx,n-size[u]);
     if (mx<mn){
         mn=mx;
         root=u;
     }
}
void getdeep( int u, int fa, int len){ //求深度
     d[++cnt]=len;
     for ( int i=0;i<son[u].size();++i){
         int v=son[u][i],w=t[u][i];
         if (v!=fa&&!f[v]){
             getdeep(v,u,w+len);
         }
     }
}
void getans( int u, int v){ //核心思路
     cnt=0;
     getdeep(u,-1,0);
     for ( int i=1;i<=cnt;++i){
         for ( int j=1;j<=cnt;++j){
             int w=d[i]+d[j];
             if (v==-1&&w<M)++ans[w];
             if (v!=-1&&w+v<M)--ans[w+v];
         }
     }
}
void work( int u){ //分治
     getans(u,-1);
     f[u]=1;
     for ( int i=0;i<son[u].size();++i){
         int v=son[u][i],w=t[u][i];
         if (!f[v]){
             getans(v,w*2);
             mn=0x7fffffff;
             n=size[v];
             getroot(v,u);
             work(root);
         }
     }
}
int main(){
     scanf ( "%d%d" ,&n,&m);
     for ( int i=1;i<n;++i){
         int u,v,w;
         scanf ( "%d%d%d" ,&u,&v,&w);
         son[u].push_back(v);
         t[u].push_back(w);
         son[v].push_back(u);
         t[v].push_back(w);
     }
     mn=0x7fffffff;
     getroot(1,-1); //求重心
     work(root);
     for ( int i=1;i<=m;++i){
         int k;
         scanf ( "%d" ,&k);
         if (ans[k]) printf ( "AYE\n" );
         else printf ( "NAY\n" );
     }
}
//大概就是这样吧,反正我就是这样跟wxk讲的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值