题目描述
【题目背景】
小 X 是远近闻名的学佛,平日里最喜欢做的事就是蒸发学水。
小 X 所在的城市 X 城是一个含有 N 个节点的无向图,同时,由于 X 国是一个发展中国家,为了节约城市建设的经费,X 国首相在建造 X 城时只建造 N – 1条边,使得城市的各个地点能够相互到达。
小 X 计划蒸发 Q 天的学水,每一天会有一名学水从 A 地走到 B 地,并在沿途各个地点留下一个水塘。此后,小 X 会从 C 地走到 B 地,并用佛光蒸发沿途的水塘。由于 X 城是一个学佛横行的城市,学水留下的水塘即使没有被小 X 蒸发,也会在第二天之前被其他学佛蒸发殆尽。
现在,小 X 想要知道,他每一天能够蒸发多少水塘呢?
输入
第一行三个整数 N、Q、NUM,分别表示 X 城地点的个数,小 X 蒸发学水的天数,以及测试点编号。注意,测试点编号是为了让选手们更方便的获得部分分,你可能不需要用到这则信息,在下发的样例中,测试点编号的含义是该样例满足某一测试点限制。
接下来 N – 1 行,每行两个整数 X、Y,表示 X 地与 Y 地之间有一条边。
接下来 Q 行,每行三个整数 A、B、C,表示一天中,有一名学水从 A 地走到B 地,而小 X 会从 C 地走到 B 地。
输出
输出 Q 行,每行一个整数,表示小 X 能够蒸发的水塘数。
样例输入
3 3 1
1 2
2 3
1 2 3
1 1 3
3 1 3
样例输出
1
1
3
提示
更多样例:下载
自己的想法:
性质1 树退化成一条链 还是很好骗分的呀
性质2 dfs一遍 记录步数即水塘数
题解:
【测试点 1-10】 对于每次询问,将两条路径上的点全部加上 1,然后统计 2 的数量,作为答案输出,询问与询问之间注意清空数组。 该算法的时间复杂度是 O(N * Q)的。
【*测试点 1-16】 对于某些数据结构学傻的同学,我提供了这一档部分分。事实上,我们可以用树链剖分的方法加速上述过程,注意清空数组时应该当 作为修改改回去,而不能直接 Memset,否则复杂度将退化。 该算法的时间复杂度是 O(N + Q * Log2 N)的。
【测试点 11-13、17-18】 这些测试点满足特殊性质 1,树退化成一条链。 那么,我们只需要判断两个闭区间交的长度即可。 该算法的时间复杂度是 O(N + Q)的。
【测试点 11、14-15、17、19】 这些测试点满足特殊性质 2,A = C。 问题简化成求 A 与 B 之间的距离,显然可以用树上倍增或 Tarjan 算法求出 A 与 B 的 LCA,那么 A 与 B 之间的距离就等于 A 的深度加 B 的深度减去 LCA 的 深度的两倍。 该算法的时间复杂度是 O(N + Q)或 O(( N + Q)* Log N)的。
【测试点 1-20】 注意到问题实际上是问树上一个点到另外两点路径的重合长度,也就是说, 这两条路径有一个端点是重合的,不妨就此性质构建算法。树上三点,或是呈一条链的形式,或是存在一个中心,使得中心到三点之间 的路径不相交。如下图,上方的两种情况是 A、B、C 点存在中心 O,下方两种情况是 A、B、C 点在一条链上。
对于呈一条链的形式,我们不妨将三个点中,位于中间的那个点称为三个点 的中心,如此一来,我们要求的答案实际上是中心到 B 点的距离。 那么,如何求中心呢?通过观察上图,我们发现,中心一定是 A、B、C 点中 两点的 LCA。并且,通过进一步观察,我们可以发现,中心是 A、B、C 点两两的 LCA 中,深度最大的点。 所以,我们只需求出中心,在求出中心到 B 点的距离即可解决本题。 该算法的时间复杂度是 O(N + Q)或 O(( N + Q)* Log N)的。
代码实现:
#include<bits/stdc++.h>
#define MAXN 200005
#define MAXLOG 20
using namespace std;
template <typename T> void read(T &x){
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+ch-'0';
x*=f;
}
int n,q,num;
vector<int> a[MAXN];
int fa[MAXN][MAXLOG],depth[MAXN];
void work(int pos,int f){
fa[pos][0]=f;
depth[pos]=depth[f]+1;
for(int i=1;i<MAXLOG;++i)
fa[pos][i]=fa[fa[pos][i-1]][i-1];
for(int i=0;i<a[pos].size();++i)
if(a[pos][i]!=f) work(a[pos][i],pos);
}
int lca(int x,int y){
if(depth[x]<depth[y]) swap(x,y);
for(int i=MAXLOG-1;i>=0;--i)
if(depth[fa[x][i]]>=depth[y]) x=fa[x][i];
if(x==y) return x;
for(int i=MAXLOG-1;i>=0;--i)
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
int dist(int x,int y){
int z=lca(x,y);
return depth[x]+depth[y]-2*depth[z];
}
int main(){
read(n),read(q),read(num);
for(int i=1;i<n;++i){
int x,y;
read(x),read(y);
a[x].push_back(y);
a[y].push_back(x);
}
work(1,0);
for(int i=1;i<=q;++i){
int A,B,C;
read(A),read(B),read(C);
int nowl=lca(A,B);
int nowll=lca(B,C);
int nowlll=lca(A,C);
if(nowl<nowll) nowl=nowll;
if(nowl<nowlll) nowl=nowlll;
cout<<dist(B,nowl)+1<<endl;
}
return 0;
}