树分治 点分治
title: 树分治 点分治
categories:
- ICPC
tags:
- null
abbrlink: fdbd89dd
date: 2023-04-08 00:00:00
给定一棵有 $n$ 个点的树,询问树上距离为 $k$ 的点对是否存在。
第一行两个数 $n,m$,n个点。
第 $2$ 到第 $n$ 行,每行三个整数 $u, v, w$,代表树上存在一条连接 $u$ 和 $v$ 边权为 $w$ 的路径。
接下来 $m$ 行,每行一个整数 $k$,代表一次询问。
对于每次询问输出一行一个字符串代表答案,存在输出 AYE
,否则输出 NAY
。
- 对于 $100%$ 的数据,保证 $1 \leq n\leq 10^4$,$1 \leq m\leq 100$,$1 \leq k \leq 10^7$,$1 \leq u, v \leq n$,$1 \leq w \leq 10^4$。
#时间复杂度O(nmlogn)
const int N=10005;
const int INF=10000005;
struct node{int v,w,ne;}e[N<<1];
int h[N],idx; //加边
int del[N],siz[N],mxs,sum,root;//求根
int dis[N],d[N],cnt; //求距离
int ans[N],q[INF],judge[INF];//求路径
int n,m,ask[N];
void add(int u,int v,int w){
e[++idx].v=v; e[idx].w=w;
e[idx].ne=h[u]; h[u]=idx;
}
void getroot(int u,int fa){
siz[u]=1;
int s=0;
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v;
if(v==fa||del[v])continue;
getroot(v,u);
siz[u]+=siz[v];
s=max(s,siz[v]);
}
s=max(s,sum-siz[u]);
if(s<mxs) mxs=s, root=u;
}
void getdis(int u,int fa){
dis[++cnt]=d[u];
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v;
if(v==fa||del[v])continue;
d[v]=d[u]+e[i].w;
getdis(v,u);
}
}
void calc(int u){
judge[0]=1;
int p=0;
// 计算经过根u的路径
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v;
if(del[v])continue;
// 求出子树v的各点到u的距离
cnt=0;
d[v]=e[i].w;
getdis(v,u);
// 枚举距离和询问,判定答案
for(int j=1;j<=cnt;++j)
for(int k=1;k<=m;++k)
if(ask[k]>=dis[j])
ans[k]|=judge[ask[k]-dis[j]];
// 记录合法距离
for(int j=1;j<=cnt;++j)
if(dis[j]<INF)
q[++p]=dis[j], judge[q[p]]=1;
}
// 清空距离数组
for(int i=1;i<=p;++i) judge[q[i]]=0;
}
void divide(int u){
// 计算经过根u的路径
calc(u);
// 对u的子树进行分治
del[u]=1;
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v;
if(del[v])continue;
mxs=sum=siz[v];
getroot(v,0); //求根
divide(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);
add(u,v,w);add(v,u,w);
}
for(int i=1;i<=m;++i)
scanf("%d",&ask[i]);
mxs=sum=n;
getroot(1,0);
getroot(root,0); //重构siz[]
divide(root);
for(int i=1;i<=m;++i)
ans[i]?puts("AYE"):puts("NAY");
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 爱飞鱼的blog!