【洛谷P3806】【模板】点分治1

1|0题目


题目链接:https://www.luogu.com.cn/problem/P3806
给定一棵有 n 个点的树,询问树上距离为 k 的点对是否存在。

2|0思路


什么彩笔快高中才学点分治 /kk。
我们可以考虑一种暴力:枚举每一个点 x,然后将 x 到每一个点 y(并且 xy 的路径中不能有之前枚举到的点)的距离 dis[y] 求出来,然后对于每一个询问 ask[i],看看 x 的其他子树内是否有长度为 ask[i]dis[y] 的边。如果有,那么该询问答案为 AYE
显然这种方法的时间复杂度为 O(n2)
发现每次取 x 的复杂度在于其子树大小。如果数退化成一条链且 x 每次取的是链最左右两端的点的话,那么时间复杂度就退化至 O(n2)
所以考虑每次取 x 时取剩余部分的重心,这样就使得每一棵子树大小都不超过那一部分的一半。那么这样的时间复杂度就降至 O(nlogn)

3|0代码


#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=10010,M=110,K=10000010,Inf=1e9; int n,m,tot,sum,rt,dis[N],maxp[N],head[N],size[N]; bool vis[N],used[K]; struct edge { int next,to,dis; }e[N*2]; struct Query { int k,ans; }ask[M]; void add(int from,int to,int dis) { e[++tot].to=to; e[tot].dis=dis; e[tot].next=head[from]; head[from]=tot; } void findrt(int x,int fa) { size[x]=1; maxp[x]=0; for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa && !vis[v]) { findrt(v,x); size[x]+=size[v]; maxp[x]=max(maxp[x],size[v]); } } maxp[x]=max(maxp[x],sum-size[x]); if (maxp[x]<maxp[rt]) rt=x; } int dfs(int x,int fa,int d) { dis[++tot]=d; size[x]=1; for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa && !vis[v]) size[x]+=dfs(v,x,d+e[i].dis); } return size[x]; } void calc(int x) { tot=1; dis[1]=0; used[0]=1; for (int i=head[x],last=0;~i;i=e[i].next) { int v=e[i].to; if (!vis[v]) dfs(v,x,e[i].dis); for (int i=last+1;i<=tot;i++) for (int j=1;j<=m;j++) if (!ask[j].ans && ask[j].k>=dis[i]) ask[j].ans=used[ask[j].k-dis[i]]; for (int i=last+1;i<=tot;i++) if (dis[i]<K) used[dis[i]]=1; last=tot; } for (int i=0;i<=tot;i++) if (dis[i]<K) used[dis[i]]=0; } void solve(int x) { calc(x); vis[x]=1; for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (!vis[v]) { rt=0; maxp[0]=Inf; sum=size[v]; findrt(v,x); solve(rt); } } } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for (int i=1,x,y,z;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } for (int i=1;i<=m;i++) scanf("%d",&ask[i].k); maxp[0]=Inf; sum=n; findrt(1,0); solve(rt); for (int i=1;i<=m;i++) if (ask[i].ans) printf("AYE\n"); else printf("NAY\n"); return 0; }

__EOF__

本文作者stoorz
本文链接https://www.cnblogs.com/stoorz/p/13228202.html
关于博主:菜死了 /fad
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   stoorz  阅读(143)  评论(1编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示