The North American Invitational Programming Contest 2016 I.Tourists(LCA求树上任意两点距离+埃式筛法)
思路来源
https://www.cnblogs.com/acjiumeng/p/7249890.html
题意
给定一棵树,求
dis(i,j)定义为树上两点距离+1,n≤2e5
题解
dfs预处理,求根节点到每一点的距离,即点i的深度depth[i]。
这里默认1号点是根节点。
然后在线二分搜索+倍增求LCA,求i和j的最近公共祖先z。
即使树退化成链表,倍增也能logn解决。
i和j的距离就是depth[i]+depth[j]-2*depth[z],
即i到z的距离+j到z的距离。
然后埃氏筛法一波就A了。
代码
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
ll cnt,head[200010];
struct edge{ll to,nex;}e[400010];
ll parent[200010][30],depth[200010];
ll ans;
void init()
{
memset(head,-1,sizeof(head));
cnt=0;
}
void add(ll u,ll v)
{
e[cnt].to=v;
e[cnt].nex=head[u];
head[u]=cnt++;
}
void dfs(ll u,ll pre,ll deep)
{
parent[u][0]=pre;
depth[u]=deep;
for (int i=head[u];~i;i=e[i].nex)
{
ll v=e[i].to;
if(v==pre) continue;
dfs(v,u,deep+1);
}
}
void double_build(ll n)
{
int i,j;
for (i=0;i+1<=25;i++)//路径倍增
for (j=1;j<=n;j++)
if (parent[j][i]<0) parent[j][i+1]=-1;
else parent[j][i+1]=parent[parent[j][i]][i];
}
ll double_query(ll u,ll v)
{
int i;
if (depth[u]>depth[v]) swap(u,v);
for (i=0;i<=25;i++)
if ((depth[v]-depth[u])>>i&1)
v=parent[v][i];
if (u==v) return u;
for (i=25;i>=0;i--) //二进制拆分思想达到倍增
if (parent[u][i]!=parent[v][i])
{
u=parent[u][i];
v=parent[v][i];
}
return parent[u][0];
}
int main()
{
ll x,y,n;
scanf("%lld",&n);
init();
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,-1,1);
double_build(n);
for(int i=1;i<=n;++i)
{
for(int j=2*i;j<=n;j+=i)
{
ll z=double_query(i,j);
ans+=1+depth[i]+depth[j]-2*(depth[z]);//距离计算
//printf("%d->%d=%lld\n",i,j,1+depth[i]+depth[j]-2*(depth[z]));
}
}
printf("%lld\n",ans);
return 0;
}