题解 P3884 【[JLOI2009]二叉树问题】
看了其他的题解,大部分(quan)都是用(主要还是因为不想写LCA)
首先,我们来看这样的一棵二叉树 (就是原题的)
现在要求查询这棵树的深度,其实就是这棵树上到根节点的最长距离
例如这棵树,不难发现,(注意断句) ,
其次,
那么如何处理第三个问题呢?
我们建的树是双向边 (废话),这里我们做一个巧妙的处理——将由
例如,我们要求
为什么呢?因为如果不是公共祖先,两顶点将无法到达;而如果不是最近的,例如我们找到了
那么此时对于前两个操作有无影响呢?肯定是没有的。因为求前两个操作的最短路时,都是从根节点出发的,只会经过数值为
综上,只要建含边权的双向边,跑两遍最短路,就可以解决问题了(源点分别是
那么时间复杂度就是
当然,这应该是个稀疏图,从理论上和实际测试中也许是电脑比较快 )
下面当然就是喜闻乐见的代码了:
#include<bits/stdc++.h>
#define in(x) x=read()
using namespace std;
const int maxN=1000;
inline int read()
{
int n=0; char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {n=n*10+ch-'0'; ch=getchar();}
return n;
}
struct Node{int to,next,value;}edge[2*maxN];
int dis[maxN+1],vis[maxN+1],head[maxN+1],tot,box[maxN+1];
void addedge(int x,int y,int w) {edge[++tot]={y,head[x],w}; head[x]=tot;}
void SPFA(int s)
{
queue<int>q; q.push(s);
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
vis[s]=1; dis[s]=0;
while(!q.empty())
{
int x=q.front(); q.pop(); vis[x]=0;
for(int i=head[x];i;i=edge[i].next)
if(dis[edge[i].to]>dis[x]+edge[i].value)
{
dis[edge[i].to]=dis[x]+edge[i].value;
if(!vis[edge[i].to])
{
vis[edge[i].to]=1;
q.push(edge[i].to);
}
}
}
}
int n,u,v,ans;
int main()
{
in(n);
for(int i=1;i<n;i++)
{
in(u); in(v);
addedge(u,v,1); addedge(v,u,2);
}
in(u); in(v); SPFA(1);
for(int i=1;i<=n;i++) box[dis[i]]++,ans=max(ans,dis[i]);
printf("%d\n",ans+1); ans=0;
for(int i=1;i<=n;i++) ans=max(ans,box[i]);
printf("%d\n",ans);
SPFA(u); printf("%d\n",dis[v]);
return 0;
}
完结撒花QwQ,如有错误欢迎指出,不胜感激 (毕竟这算法是脑补出来的)