算法用途
处理树上路径之间的一些关系时,如果采用暴力,往往时间复杂度不能够接受。而点分治能够很好的解决这一类问题
算法基本步骤及实现
以洛谷P3806为例
1.找根
第一步当然是把无根树变成有根树。但是把哪个点当作根,这是很讲究的。
因为树是递归操作的,因此我们希望递归的次数越小越好。
定义树的重心:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心。
这就是我们要找的点。
那要怎么找重心呢?
DFS算出以每个节点为根的子树大小,同时记录每个节点最大子树的大小。
如果当前节点的最大子树大小<当前根的最大子树大小,更新当前根。
给个代码参考:
void dfsrt(int x,int fa){
sum[x]=1; ma[x]=0;//sum存子树大小,ma存最大子树的大小
for (int i=h[x];i;i=ed[i].next)
if (ed[i].to!=fa&&!f[ed[i].to]){
int v=ed[i].to; dfsrt(v,x);
sum[x]+=sum[v]; ma[x]=max(ma[x],sum[v]);
}
ma[x]=max(ma[x],t-sum[x]);//还有另一个子树(即它的爸爸)
if (ma[x]<ma[rt]) rt=x;//rt为根
}
2.处理经过当前根的路径
经过当前根的路径=所有路径-没有经过当前根的路径
3.删除根节点
4.递归处理其子树
这道题让我们求树上距离为k的点对,那么我们先递归求出每个点到根的距离,然后两两配对即可。时间复杂度 O(n2) (n为这棵树的节点个数)
但是我们可以优化一下。先给所有距离排序,再从两头进行配对。正确性显然。时间复杂度 O(nlogn) 。
然而这道题有100个k,做100次点分治显然不可能。注意到k<=1e7,那么直接预处理距离为1~1e7的点对个数,然后 O(1) 回答。
void dfsdep(int x,int fa){//计算到根节点的距离
dep[++p]=dis[x];
for (int i=h[x];i;i=ed[i].next)
if (ed[i].to!=fa&&!f[ed[i].to]){
dis[ed[i].to]=dis[x]+ed[i].dis;
dfsdep(ed[i].to,x);
}
}
void dfssum(int x,int v,bool flag){//计算满足条件的点对个数
dis[x]=v; p=0; dfsdep(x,0);
for (int i=1;i<=p;i++)
for (int j=1;j<=p;j++)
if (flag&&dep[i]+dep[j]<=inf) num[dep[i]+dep[j]]++;//预处理
else if (dep[i]+dep[j]<=inf) num[dep[i]+dep[j]]--;
}
void dfsans(int x){//递归
f[x]=true;//删除根节点
dfssum(x,0,1);//总路径
for (int i=h[x];i;i=ed[i].next)//递归其子树
if (!f[ed[i].to]){
int v=ed[i].to; dfssum(v,ed[i].dis,0);//不合法路径
rt=0; t=sum[v]; dfsrt(v,0); dfsans(rt);
}
}
完整代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 10000
#define inf 10000000
using namespace std;
struct edge{
int next,to,dis;
}ed[MAXN*2+5];
int n,k,m,p,t,rt,ans;
int h[MAXN+5],dis[MAXN+5],dep[MAXN+5],ma[MAXN+5],sum[MAXN+5],num[inf+5];
bool f[MAXN+5];
inline char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF; return *l++;
}
inline int _read(){
int num=0; char ch=readc();
while (ch<'0'||ch>'9') ch=readc();
while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }
return num;
}
void addedge(int x,int y,int z){
ed[++k].next=h[x]; ed[k].to=y; ed[k].dis=z; h[x]=k;
}
void dfsrt(int x,int fa){
sum[x]=1; ma[x]=0;
for (int i=h[x];i;i=ed[i].next)
if (ed[i].to!=fa&&!f[ed[i].to]){
int v=ed[i].to; dfsrt(v,x);
sum[x]+=sum[v]; ma[x]=max(ma[x],sum[v]);
}
ma[x]=max(ma[x],t-sum[x]);
if (ma[x]<ma[rt]) rt=x;
}
void dfsdep(int x,int fa){
dep[++p]=dis[x];
for (int i=h[x];i;i=ed[i].next)
if (ed[i].to!=fa&&!f[ed[i].to]){
dis[ed[i].to]=dis[x]+ed[i].dis;
dfsdep(ed[i].to,x);
}
}
void dfssum(int x,int v,bool flag){
dis[x]=v; p=0; dfsdep(x,0);
for (int i=1;i<=p;i++)
for (int j=1;j<=p;j++)
if (flag&&dep[i]+dep[j]<=inf) num[dep[i]+dep[j]]++;
else if (dep[i]+dep[j]<=inf) num[dep[i]+dep[j]]--;
}
void dfsans(int x){
f[x]=true; dfssum(x,0,1);
for (int i=h[x];i;i=ed[i].next)
if (!f[ed[i].to]){
int v=ed[i].to; dfssum(v,ed[i].dis,0);
rt=0; t=sum[v]; dfsrt(v,0); dfsans(rt);
}
}
int main(){
n=_read(); m=_read();
for (int i=1;i<n;i++){
int u=_read(),v=_read(),d=_read();
addedge(u,v,d); addedge(v,u,d);
}
t=n; ma[0]=0x7fffffff; dfsrt(1,0); dfsans(rt);
for (int i=1;i<=m;i++){
int u=_read();
if (num[u]) printf("AYE\n");
else printf("NAY\n");
}
return 0;
}