【Luogu P3806】点分治
点分治主要用于解决无根树上的一些路径统计问题。
点分治的基本步骤:
- 选定一个点作为根,把路径分为两类,一类是经过是根节点的,另一类是不经过根节点的。
- dfs处理处这一棵路径的信息。
- 运用某些方法(双指针,树状数组,桶)等统计方法,确认答案。
- 删除根节点,对子树重复进行递归处理。
显然,第一类路径就是在根节点不同子树的路径,第二类就是位于根节点一棵子树内的路径。
第二类我们递归处理即可,关键是第一类应当如何计算。
对于这题我们有两种方法,计算出答案,分别是双指针和桶。
鉴于数据的特殊性,双指针更合适一些,桶的代码取决于权值的值域,容易RE,改换成平衡树可能会使代码变得复杂。
双指针:
处理出\(dis[i]\),即根节点到\(i\)节点的距离,和\(belong[i]\),即节点\(i\)属于根节点的哪一棵子树。
然后按照把所有节点放进一个数组,按照\(dis[i]\)的大小排序,利用双指针求解即可。
复杂度\(O(nlog_2n)\)
void calc()
{
for (int i=1;i<=m;i++)//遍历每一个询问进行确认
{
int k=rec[i],l=1,r=tot;
if (ans[i]) continue;
while (l<r)
{
if (dis[a[l]]+dis[a[r]]>k) r--;
else if (dis[a[l]]+dis[a[r]]<k) l++;
else if (dis[a[l]]+dis[a[r]]==k&&belong[a[l]]==belong[a[r]])
{
if (dis[a[r]]==dis[a[r-1]]) r--;
else l++;
}
else
{
ans[i]=true;
break;
}
}
}
}
另外一个问题就在于取哪一个点作为根更加合适。
显然,如果我们任意取,当树退化成一条链时,复杂度便会变得不可接受。
证明过程显然,自己想象一下。
那么我们要尽可能的使各个子树平衡,才能让递归层数尽可能少。
显然有一种点符合这个性质,也就是重心。
那么我们每一次取当前子树的重心即可。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct data
{
int to,val,next;
}edge[20005];
const int inf=15000005;
int root,cnt,head[10005],a[10005],tot,belong[10005],ans[10005],rec[105],u,v,w,n,m,size[10005],recsize=0x3f3f3f3f,dis[10005],vis[10005],sum;
void add(int u,int v,int w)
{
edge[++cnt].to=v;
edge[cnt].next=head[u];
edge[cnt].val=w;
head[u]=cnt;
}
void get_root(int u,int fa)
{
int maxsize=0;
size[u]=1;
for (int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if (v==fa||vis[v]) continue;
get_root(v,u);
size[u]+=size[v];
maxsize=max(size[v],maxsize);
}
maxsize=max(maxsize,sum-size[u]);
if (maxsize<recsize)
{
root=u;
recsize=maxsize;
}
}
void dfs(int now,int rt,int fa)
{
a[++tot]=now;
belong[now]=cnt;
size[now]=1;
for (int i=head[now];i;i=edge[i].next)
{
int v=edge[i].to;
if (v==fa||vis[v]) continue;
if (now==rt) cnt++;
dis[v]=dis[now]+edge[i].val;
dfs(v,rt,now);
size[now]+=size[v];
}
}
void calc()
{
for (int i=1;i<=m;i++)
{
int k=rec[i],l=1,r=tot;
if (ans[i]) continue;
while (l<r)
{
if (dis[a[l]]+dis[a[r]]>k) r--;
else if (dis[a[l]]+dis[a[r]]<k) l++;
else if (dis[a[l]]+dis[a[r]]==k&&belong[a[l]]==belong[a[r]])
{
if (dis[a[r]]==dis[a[r-1]]) r--;
else l++;
}
else
{
ans[i]=true;
break;
}
}
}
}
bool cmp(int x,int y)
{
return dis[x]<dis[y];
}
void work()
{
cnt=0;dis[root]=0;tot=0;vis[root]=true;
dfs(root,root,0);
sort(a+1,a+1+tot,cmp);
calc();
int t=root;
for (int i=head[t];i;i=edge[i].next)
{
if (vis[edge[i].to]) continue;
recsize=0x3f3f3f3f;
sum=size[edge[i].to];
get_root(edge[i].to,t);
work();
}
}
int read()
{
char c=getchar();int num=0;
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') {num*=10;num+=c-48;c=getchar();}
return num;
}
int main()
{
n=read();m=read();
for (int i=1;i<n;i++)
{
u=read(),v=read(),w=read();
add(u,v,w);add(v,u,w);
}
for (int i=1;i<=m;i++)
{
int k;
k=read();
rec[i]=k;
}
sum=n;
get_root(1,0);
work();
for (int i=1;i<=m;i++)
printf(ans[i]?"AYE\n":"NAY\n");
return 0;
}