题目
给定一棵有n(n<=1e4)个点的树
询问树上距离为k(k<=1e7)的点对是否存在
询问m(m<=100)组
思路来源
https://www.cnblogs.com/bztMinamoto/p/9489473.html
https://baijiahao.baidu.com/s?id=1608747083240620752&wfr=spider&for=pc
https://www.cnblogs.com/PinkRabbit/p/8593080.html(点分治学习博客)
题解
入坑点分治,利用重心降复杂度
暂时只遇到解决树上路径的问题,
每次只统计必经过当前重心根的答案,
用在重心u的子树里任取的答案减去在儿子v的子树里任取的答案
每次操作分三步,不断递归执行①②③
①找重心
②确定重心到子树内各点的距离
③统计路径必经重心这个点的答案
点分治大部分是板子,主要改cal函数即可
这里重搜了m次,复杂度,
注意在重搜之前对若干变量(如vis[])的初始化
还有多组数据对边的init()
存在cal函数里特判路径是否来自同一棵子树的做法,
开一个b[]数组来判点来自哪里,
用双指针判,判到路径必经根时再统计
后续:
发现这个题从1s改成400ms了,
原代码过不去了,毒瘤……
如何避开错误的点分治写法
思路来源:https://liu-cheng-ao.blog.uoj.ac/blog/2969
不能每次直接sz=siz[v],这样可能出现a是b的父亲,b是c的父亲,第一次以a为根dfs,重心是c,
第二次对b所在的子树进行点分治时,sz[b]的值是错的(此时应该把a看错是儿子,错误的将c看成是儿子)
思路来源说这样的复杂度好像是对的……但总感觉好像是能卡掉的……
所以每次确定重心前,先重新对v进行一下dfs确定正确的siz,再点分治下去,这样就没问题了
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=1e4+10;
const int K=1e7+10;
int head[N],cnt;
struct edge{int v,nex;ll w;}e[2*N];
void add(int u,int v,ll w){e[++cnt]=edge{v,head[u],w};head[u]=cnt;}
bool vis[N];
int n,m,k,query[N],l,r,u,v;
int siz,f[N],sz[N],rt;
ll res,q[N],d[N],w;
void init(){
cnt=0;
memset(head,0,sizeof head);
}
//找下一次的重心rt
void getrt(int u,int fa,bool op){
f[u]=0;sz[u]=1;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(v==fa||vis[v])continue;
getrt(v,u,op);
f[u]=max(f[u],sz[v]);
sz[u]+=sz[v];
}
if(op){
f[u]=max(f[u],siz-sz[u]);
if(f[u]<f[rt])rt=u;
}
}
//计算重心u到子树内每个点的距离
void getdis(int u,int fa){
q[++r]=d[u];
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
ll w=e[i].w;
if(v==fa||vis[v])continue;
d[v]=d[u]+w;
getdis(v,u);
}
}
//计算以u为根的子树的答案
ll cal(int u,ll w){
r=0;d[u]=w;
getdis(u,0);
l=1;ll ans=0;
sort(q+1,q+r+1);
while(l<r){
if(k-q[l]<q[l])break;
ll v=upper_bound(q+l+1,q+r+1,k-q[l])-lower_bound(q+l+1,q+r+1,k-q[l]);
ans+=v;l++;
}
return ans;
}
void dfs(int u){
//每次用在u的子树里任取减去在v的子树里的答案
//每次只计算 必经过u的答案
res+=cal(u,0);
vis[u]=1;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
ll w=e[i].w;
if(vis[v])continue;
res-=cal(v,w);
getrt(v,u,0);//获得正确的sz[v]
siz=sz[v];rt=0;
getrt(v,u,1);
dfs(rt);
}
}
int main(){
scanf("%d%d",&n,&m);
init();
for(int i=1;i<n;++i){
scanf("%d%d%lld",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
for(int i=1;i<=m;++i){
scanf("%d",&query[i]);
}
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j)
vis[j]=0;
res=0;k=query[i];
f[0]=siz=n;rt=0;
getrt(1,0,1),dfs(rt);
puts(res?"AYE":"NAY");
}
return 0;
}