虚树神马的网上已经讲了不少了 , 这里就多提啦 , 推荐看这个小伙伴的博客
本题是一个虚树的裸题 , 就不提示了。一般来说我不写这种题解烂大街的题目 , 但这次尝试打破了我以前的一个思维惯性 , 关于时间复杂度的误区。
为什么这题我写树链剖分求LCA呢? 因为我算错空间辣-_-#
当时我正在算倍增算法的空间 , 1000000∗20→2∗108 然后我就不敢写 n∗log(n) 的倍增啦QAQ
不要紧 , 反正链剖空间低 , 我只有委曲求全的写了 n∗log(n)2 链剖 , 发现时间还挺快的 , 比hzwer的快2s. 当时就想 ,不会吧 , 我写的可是 n∗log(n)2 的方法啊……
此时我想起某次写倍增被卡时(最后被逼把倍增改成3进制才过)的经历 , 这次我决定试一试这两者的时间差别。 当我把 lca 改成倍增写法后 , 时间狂翻3倍QAQ。 我尝试了各种优化措施 , 但效果均不明显 , 难道 n∗log(n) 的倍增算法常数真的这么大吗? 其实仔细看看倍增的结构 , 你会发现 , 常数很小的样子 , 就是几个循环了事啊……
所以 , 这只能说明树剖的常数小 , 至于有多小 , 大家都可以脑补(某集训队论文分析 , 常数最大 12 ) , 但如果查询的两个点很接近 , 那么可以快速出解。 但是倍增的时间复杂度的自由空间很小 , 所以在此题求 lca 一个理论上 n∗log(n)2 的算法战胜了一个理论上 n∗log(n) 的算法。
我的代码里有两种写法 , 大家可以随意调试:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <cassert>
using namespace std;
const int maxn = 1e6+1e2;
const int INF = 0x3f3f3f3f;
int n , m , dfsCnt , fa[maxn] , id[maxn] , bl[maxn] , dep[maxn] , Size[maxn] , book[maxn] , w[maxn];
vector<int> g[maxn] , f[maxn];
//int ace[maxn][20];
//void pre()
//{
// for(int i=1;i<=n;i++) ace[i][0] = fa[i];
// for(int i=1,j;i<20;i++) for(int k=1;k<=n;k++)
// {
// j = ace[k][i-1];
// if(!j) continue;
// ace[k][i] = ace[j][i-1];
// }
//}
__inline int re() {
int n = 0, ch = getchar(); bool flag = false;
while(!isdigit(ch)) flag |= ch == '-', ch = getchar();
while(isdigit(ch)) n = n * 10 + ch - '0', ch = getchar();
return flag ? -n : n;
}
void dfs(int x)
{
id[x] = ++dfsCnt;
Size[x] = 1;
for(int i=0,t;i<g[x].size();i++)
{
t = g[x][i];
if(t == fa[x]) continue;
fa[t] = x;
dep[t] = dep[x] + 1;
dfs(t);
Size[x] += Size[t];
}
}
void dfs(int x , int num)
{
bl[x] = num;
int mx=0 , w;
for(int i=0,t;i<g[x].size();i++)
{
t = g[x][i];
if(t == fa[x]) continue;
if(mx < Size[t]) w = t , mx = Size[t];
}
if(mx) dfs(w, num);
for(int i=0,t;i<g[x].size();i++)
{
t = g[x][i];
if(t == fa[x] || t == w) continue;
dfs(t, t);
}
}
//int lca(int x , int y)
//{
// if(dep[x] > dep[y]) swap(x, y);
// for(int i=log2(dep[y]+1);i>=0;i--) if(ace[y][i] && dep[ace[y][i]] >= dep[x]) y = ace[y][i];
//
// if(x == y) return x;
//
// for(int i=log2(dep[x]+1),j,k;i>=0;i--)
// {
// j = ace[x][i];
// k = ace[y][i];
// if(j == k) continue;
// x = j; y = k;
// }
//
// return fa[x];
//}
int lca(int x , int y)
{
int f1 , f2;
while(true)
{
f1 = bl[x];
f2 = bl[y];
if(f1 == f2) break;
if(dep[f1] < dep[f2]) y = fa[f2];
else x = fa[f1];
}
return dep[x] < dep[y] ? x : y;
}
long long r1;
int r2 , r3 , mn[maxn] , mx[maxn] , s[maxn];
void getRes(int x)
{
if(book[x])
{
s[x] = 1;
r1 += 1LL*dep[x]*m;
mx[x] = mn[x] = dep[x];
}
for(int i=0,j;i<f[x].size();i++)
{
j = f[x][i];
getRes(j);
s[x] += s[j];
r1 -= 1LL*s[j]*s[j]*(dep[j] - dep[x]);
r2 = min(r2 , mn[x] - dep[x] + mn[j] - dep[x]);
r3 = max(r3 , mx[x] - dep[x] + mx[j] - dep[x]);
mn[x] = min(mn[x] , mn[j]);
mx[x] = max(mx[x] , mx[j]);
}
f[x].clear();
}
void clear(int x)
{
for(int i=0,j;i<f[x].size();i++)
{
j = f[x][i];
clear(j);
}
mn[x] = INF; mx[x] = -INF; s[x] = 0;
}
int q[maxn] , t;
bool cmp(int x , int y) { return id[x] < id[y]; }
void solve()
{
sort(w+1, w+1+m, cmp);
t = 0;
for(int i=1,j;i<=m;i++)
{
if(!t) q[t++] = w[i];
else
{
if(lca(q[t-1], w[i]) == q[t-1]) q[t++] = w[i];
else
{
while(t>1 && dep[lca(q[t-2], w[i])] < dep[q[t-2]])
{
f[q[t-2]].push_back(q[t-1]);
t--;
}
f[j = lca(q[t-1], w[i])].push_back(q[t-1]) , t--;
if(!t || q[t-1]!=j) q[t++] = j;
if(!t || q[t-1]!=w[i]) q[t++] = w[i];
}
}
}
while(t>1) f[q[t-2]].push_back(q[t-1]) , t--;
r1 = 0; r2 = INF; r3 = -INF;
clear(q[0]);
getRes(q[0]);
printf("%lld %d %d\n" , r1-1LL*dep[q[0]]*m*m , r2 , r3);
}
int main(int argc, char *argv[]) {
cin>>n;
for(int i=1,j,k;i<n;i++)
{
j = re(); k = re();
g[j].push_back(k);
g[k].push_back(j);
}
dfs(1);
dfs(1, 1);
// pre();
int q;
cin>>q;
while(q--)
{
m = re();
for(int i=1;i<=m;i++) w[i] = re() , book[w[i]] = 1;
solve();
for(int i=1;i<=m;i++) book[w[i]] = 0;
}
return 0;
}