题解 P2912 【[USACO08OCT]牧场散步Pasture Walking】

shadowice1984

2017-10-06 17:48:04

Solution

蒟蒻第一次写倍增。。。 这里简单说一下倍增的原理 任意一个正整数,都可以表示为几个2的n次幂之和。 根据这个原理,我们只需知道一个点上方的第2的n次方个祖先,就可以求出其所有祖先 (e。g。求第3个祖先可以表示为该点上方第二个祖先的第1个祖先) 同理,知道一个点上方的第2的n次方个祖先到该点的距离,即可求出该点到所有祖先的距离 那么,令father【i】【j】表示点i上方第2的j次方的祖先编号,d【i】【j】表示到father【i】【j】的距离 由2^n=2^(n-1)+2^(n-1); 可得father【i】【j】=father【father【i】【j-1】】【j-1】;d【i】【j】=d【i】【j-1】+d【father【i】【j-1】】【j-1】; 上代码~ ```cpp #include<stdio.h> #include<algorithm> using namespace std; struct data//邻接表 { int next;int v;int val; }edge[2010];int cnt;int alist[1010]; void add(int u,int v,int val) { edge[++cnt].v=v; edge[cnt].val=val; edge[cnt].next=alist[u]; alist[u]=cnt;return; } int d[1010][15]; int father[1010][15]; int depth[1010];//深度数组,作用一会说 int n;int q; void dfs(int x)//深搜,完成祖先和祖先距离和深度关系 { for(int i=1;i<15;i++) { if(father[x][i-1]!=0)//递推f和d { father[x][i]=father[father[x][i-1]][i-1]; d[x][i]=d[x][i-1]+d[father[x][i-1]][i-1]; } else break;//0表示没有这个祖先,即距离太大了,那么更大的2^(n+1)也是0 } int next=alist[x]; while(next)//遍历出边 { int v=edge[next].v;int val=edge[next].val; if(v!=father[x][0])//显然不能爸爸是儿子的儿子,这就乱套了 { father[v][0]=x;//初始化儿子 d[v][0]=val;depth[v]=depth[x]+1; dfs(v); } next=edge[next].next; } return; } int lca(int u,int v)//标准的lca模板 { int res=0;//一定要赋值 if(depth[u]<depth[v])swap(u,v);//钦定u更低 int delta=depth[u]-depth[v]; for(int i=0;i<15;i++) { if(delta&(1<<i))//把depth【u】和depth【v】的差拆成2的n次幂之和 { res+=d[u][i];//更改u的同时更改d u=father[u][i];//更改u } } if(u==v)return res;//如果v是直系祖先,返回 for(int i=14;i>=0;i--)//先选大的二进制位,有点贪心的意思 { if(father[u][i]!=father[v][i])//移动 { res+=d[u][i]+d[v][i]; u=father[u][i]; v=father[v][i]; } } res+=d[u][0]+d[v][0];//最后father【u】【0】才是lca,因此加上u,v到lca距离 return res; } int main() { scanf("%d%d",&n,&q); for(int i=0;i<n-1;i++) { int u;int v;int val; scanf("%d%d%d",&u,&v,&val); add(u,v,val);//双向边 add(v,u,val); } dfs(1);//dfs for(int i=0;i<q;i++) { int u;int v; scanf("%d%d",&u,&v); printf("%d\n",lca(u,v));//处理询问 } return 0; } ```