The North American Invitational Programming Contest 2016 I.Tourists(LCA求樹上任意兩點距離+埃式篩法)
阿新 • • 發佈:2018-12-13
思路來源
題意
給定一棵樹,求
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; }