题目链接
https://vjudge.net/problem/CodeForces-519E
解题思路
整体思路:LCA
详细思路:
前置知识: 里面的链接是知识点的讲解
具体思路:
我们分两种大情况
小情况1:若u==LCA(u,v) || v==LCA(u,v)
u==v
,则答案直接就是n;
小情况2:若u!=v
,会出现如图情况:
显然,绿色的节点是满足题意的节点,总结而言,先找到处于u,v中间深度的节点,用以这个节点为根的子树包含的节点数减去以“u所在路径上这个节点的直接子节点”为根的子树包含的节点数,即为答案。
这种大情况,相当于u,v的路径跨越了它们的LCA节点。u!=LCA(u,v) && v!=LCA(u,v)
小情况1:若deep[u]==dp[v]
,即u,v的深度相同,还是画图形象:
就挺形象吧,我还真不知道怎么描述好,这一画图直接明明白白的。
对于这种情况,总结而言,先找到u所在路径的lca的直接子节点,记为x,v所在路径的lca的直接子节点,记为y(在本图中x,y分别为u,v的直接父亲节点),用全部节点数n-以x为根的子树包含的节点数-以y为根的子树包含的节点数。
小情况2:若deep[u]!=dp[v]
,即u,v的深度不相同,再来个图:
答案为,先找到处于u,v路径中间位置的节点,用以此节点为根的树包含的节点数-以“u所在路径上的中间位置节点的直接儿子”为根的树包含的节点数。
小总结:
这些结论的得出其实不难,并不需要严谨的证明,多举几组样例就可以了。而且,对于这种题而言,我们要讨论的大情况无非就是两节点的相对位置,也就是在同一根树枝上,或者不在同一根树枝上,小情况可以慢慢整理样例得出。
AC代码
#include<bits/stdc++.h> using namespace std; const int N=1e5+5; int n,m,ans,u,v,cnt; int dp[N],head[N],fa[N][20],lg[N],sumson[N]; struct edge{int v,next;}e[N<<1]; //快读、链式前向星加边、获取lg数值(相当于先预处理一下移位操作) inline int read(){int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+(ch-'0');return x*f;} inline void add(int u,int v){e[++cnt].v=v;e[cnt].next=head[u];head[u]=cnt;} inline void getlg(){for(int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);} //dfs对fa数组赋值、对dp数组赋值、对sumson数组赋值 void dfs(int x,int fath) { dp[x]=dp[fath]+1; fa[x][0]=fath; sumson[x]=1; for(int i=1;i<=lg[dp[x]];i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for(int i=head[x];i;i=e[i].next) { int v=e[i].v; if(fath==v) continue; dfs(v,x); sumson[x]+=sumson[v]; } } //倍增法求LCA int LCA(int u,int v) { while(dp[u]>dp[v]) u=fa[u][lg[dp[u]-dp[v]]-1]; if(u==v) return u; for(int i=lg[dp[u]];i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; return fa[u][0]; } //寻找距离节点x为dis的祖先节点 int find(int x,int dis) { for(int i=lg[dp[x]];i>=0;i--) { if(dis-(1<<i)<0) continue; x=fa[x][i];dis-=(1<<i); } return x; } int main() { n=read(); for(int i=1;i<n;i++) {u=read();v=read();add(u,v);add(v,u);} getlg();dfs(1,0); m=read(); while(m--) { u=read();v=read(); if(dp[u]<dp[v]) swap(u,v);//保证u节点的深度不小于v节点的深度 int lca=LCA(u,v),dis=dp[u]+dp[v]-dp[lca]-dp[lca];//求u与v之间的(最短)距离 if(dis&1) {puts("0");continue;}//距离为奇数,不存在一个节点满足题意 dis>>=1;//距离除以2,得到离u,v最近的满足题意的节点到u,v的距离 if(v==lca)//如果深度较浅的节点v是u,v的LCA时,说明u,v在同一条支路上 { if(u==v) ans=n;//特判,u,v重合,所有点都满足题意//不特判结果出错 else { int midson=find(u,dis-1);//找到在u所在路径上,离u,v最近的满足题意的节点的子节点 ans=sumson[fa[midson][0]]-sumson[midson];//midson的父亲节点为根的树包含的节点数量与midson节点为根的树包含的节点数量之差 } } else { if(dp[u]==dp[v])//u,v的LCA即为离u,v最近的满足题意的节点 { int umidson=find(u,dis-1),vmidson=find(v,dis-1);//找到u,v所在路径上,u,v的LCA的子节点 ans=n-sumson[umidson]-sumson[vmidson]; } else { int midson=find(u,dis-1);//u,v深度不相等 ans=sumson[fa[midson][0]]-sumson[midson];//与v==lca&&v!=u的输出表达式是一样的 } } printf("%d\n",ans); } return 0; }
个人总结
没AC,一直wa3。
主要原因:情况大致分对,但是并不细致,且缺少特判。
不过find函数及思想实现的还不错(大佬勿喷QWQ)。
写了俩小时题解,不打算留个赞再走吗,大佬QWQ
REF
https://blog.nowcoder.net/n/c37b1e71192e452db937c212bdff939e
https://www.cnblogs.com/qixingzhi/p/9302038.html