[点分治]luogu P3806 【模板】点分治1
题目描述
给定一棵有n个点的树
询问树上距离为k的点对是否存在。
输入输出格式
输入格式:
n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径
接下来m行每行询问一个K
输出格式:
对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号)
输入输出样例
输入样例
2 1 1 2 2 2
输出样例
AYE
说明
对于30%的数据n<=100
对于60%的数据n<=1000,m<=50
对于100%的数据n<=10000,m<=100,c<=1000,K<=10000000
分析
其实点分治的主体思想也就是对于树的重心进行分治,也就是找到重心以后,再找子树的重心,这样效率最大化
分治的内容千变万化,只有熟练才能分析出来(常用的有容斥原理)
#include <iostream> #include <cstdio> using namespace std; const int N=1e5+10; struct Edge { int u,v,w,nx; }g[N]; int list[N],cnt; int sz[N],bg[N],rt,p[N],pcnt,cd[10000010],d[N]; bool b[N]; int n,m; void Add(int u,int v,int w) { g[++cnt]=(Edge){u,v,w,list[u]};list[u]=cnt; } void Get_Root(int u,int fa) { sz[u]=1;bg[u]=0; for (int i=list[u];i;i=g[i].nx) if (g[i].v!=fa&&!b[g[i].v]) { Get_Root(g[i].v,u); sz[u]+=sz[g[i].v]; bg[u]=max(bg[u],sz[g[i].v]); } bg[u]=max(bg[u],n-bg[u]); if (bg[u]<bg[rt]) rt=u; } void Get_Dist(int u,int fa,int len) { p[++pcnt]=len; for (int i=list[u];i;i=g[i].nx) if (g[i].v!=fa&&!b[g[i].v]) Get_Dist(g[i].v,u,len+g[i].w); } void Calc(int u,int len,int add) { pcnt=0; Get_Dist(u,0,len); for (int i=2;i<=pcnt;i++) for (int j=1;j<i;j++) cd[p[i]+p[j]]+=add; } void Solve(int u) { b[u]=1; Calc(u,0,1); for (int i=list[u];i;i=g[i].nx) if (!b[g[i].v]) { Calc(g[i].v,g[i].w,-1); rt=0;Get_Root(g[i].v,u); Solve(rt); } } int main() { scanf("%d%d",&n,&m); for (int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),Add(u,v,w),Add(v,u,w); bg[0]=2147483647; Get_Root(1,0); Solve(rt); for (int i=1,k;i<=m;i++) { scanf("%d",&k); printf(!cd[k]?"NAY\n":"AYE\n"); } }
在日渐沉没的世界里,我发现了你。