BZOJ1468: Tree & BZOJ3365: [Usaco2004 Feb]Distance Statistics 路程统计

时间:2023-03-08 23:23:39
BZOJ1468: Tree & BZOJ3365: [Usaco2004 Feb]Distance Statistics 路程统计

【传送门:BZOJ1468&BZOJ3365


简要题意:

  给出一棵n个点的树,和每条边的边权,求出有多少个点对的距离<=k


题解:

  点分治模板题

  点分治的主要步骤:

  1、首先选取一个点,把无根树变成有根树。 那么如何选点呢? ——树形DP

  因为树是递归定义的,所以我们当然希望递归的层数最小。 每次选取的点,要保证与此点相连的结点数最多的连通块的结点数最小,我们把这个点叫做“重心”

  那么找到一颗树的重心有以下算法:

  (1)dfs一次,算出以每个点为根的子树大小

  (2)记录以每个结点为根的最大子树的大小

  (3)判断:如果以当前结点为根的最大子树大小比当前根更优,更新当前根

  2、处理联通块中通过根结点的路径

  3、标记根结点(相当于处理过后,将根结点从子树中删除)

  4、递归处理以当前点的儿子为根的每棵子树


参考代码(一):

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
int x,y,d,next;
}a[];int len,last[];
void ins(int x,int y,int d)
{
len++;
a[len].x=x;a[len].y=y;a[len].d=d;
a[len].next=last[x];last[x]=len;
}
int tot[],root,sum,ms[];
bool v[];
void getroot(int x,int fa)
{
tot[x]=;ms[x]=;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa&&v[y]==false)
{
getroot(y,x);
tot[x]+=tot[y];
ms[x]=max(ms[x],tot[y]);
}
}
ms[x]=max(ms[x],sum-tot[x]);
if(ms[root]>ms[x]) root=x;
}
int dep[],id;
int dd[];
void getdep(int x,int fa)
{
dep[++id]=dd[x];
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa&&v[y]==false)
{
dd[y]=dd[x]+a[k].d;
getdep(y,x);
}
}
}
int ans=;
int k;
int cal(int x,int d)
{
dd[x]=d;id=;
getdep(x,);
sort(dep+,dep+id+);
int l=,r=id,c=;
while(l<r)
{
if(dep[l]+dep[r]<=k){c+=r-l;l++;}
else r--;
}
return c;
}
void solve(int x)
{
ans+=cal(x,);
v[x]=true;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(v[y]==false)
{
ans-=cal(y,a[k].d);
sum=tot[y];
root=;getroot(y,x);
solve(root);
}
}
}
int main()
{
int n;
scanf("%d",&n);
len=;memset(last,,sizeof(last));
for(int i=;i<n;i++)
{
int x,y,d;
scanf("%d%d%d",&x,&y,&d);
ins(x,y,d);ins(y,x,d);
}
scanf("%d",&k);
memset(v,false,sizeof(v));
ans=;
sum=tot[]=n;
ms[]=<<-;
root=;getroot(,);
solve(root);
printf("%d\n",ans);
return ;
}

参考代码(二):

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
int x,y,d,next;
}a[];int len,last[];
void ins(int x,int y,int d)
{
len++;
a[len].x=x;a[len].y=y;a[len].d=d;
a[len].next=last[x];last[x]=len;
}
int tot[],root,sum,ms[];
bool v[];
void getroot(int x,int fa)
{
tot[x]=;ms[x]=;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa&&v[y]==false)
{
getroot(y,x);
tot[x]+=tot[y];
ms[x]=max(ms[x],tot[y]);
}
}
ms[x]=max(ms[x],sum-tot[x]);
if(ms[root]>ms[x]) root=x;
}
int dep[],id;
int dd[];
void getdep(int x,int fa)
{
dep[++id]=dd[x];
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa&&v[y]==false)
{
dd[y]=dd[x]+a[k].d;
getdep(y,x);
}
}
}
int ans=;
int k;
int cal(int x,int d)
{
dd[x]=d;id=;
getdep(x,);
sort(dep+,dep+id+);
int l=,r=id,c=;
while(l<r)
{
if(dep[l]+dep[r]<=k){c+=r-l;l++;}
else r--;
}
return c;
}
void solve(int x)
{
ans+=cal(x,);
v[x]=true;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(v[y]==false)
{
ans-=cal(y,a[k].d);
sum=tot[y];
root=;getroot(y,x);
solve(root);
}
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
len=;memset(last,,sizeof(last));
for(int i=;i<=m;i++)
{
int x,y,d;char st[];
scanf("%d%d%d%s",&x,&y,&d,st+);
ins(x,y,d);ins(y,x,d);
}
scanf("%d",&k);
memset(v,false,sizeof(v));
ans=;
sum=tot[]=n;
ms[]=<<-;
root=;getroot(,);
solve(root);
printf("%d\n",ans);
return ;
}