BZOJ 3612 HEOI2014 大工程 树链剖分求LCA的优越

来源:互联网 发布:差分方程matlab编程 编辑:程序博客网 时间:2024/05/17 08:46

虚树神马的网上已经讲了不少了 , 这里就多提啦 , 推荐看这个小伙伴的博客

本题是一个虚树的裸题 , 就不提示了。一般来说我不写这种题解烂大街的题目 , 但这次尝试打破了我以前的一个思维惯性 , 关于时间复杂度的误区。

为什么这题我写树链剖分求LCA呢? 因为我算错空间辣-_-#

当时我正在算倍增算法的空间 , 1000000202108 然后我就不敢写nlog(n)的倍增啦QAQ

不要紧 , 反正链剖空间低 , 我只有委曲求全的写了nlog(n)2链剖 , 发现时间还挺快的 , 比hzwer的快2s. 当时就想 ,不会吧 , 我写的可是nlog(n)2的方法啊……

此时我想起某次写倍增被卡时(最后被逼把倍增改成3进制才过)的经历 , 这次我决定试一试这两者的时间差别。 当我把lca改成倍增写法后 , 时间狂翻3倍QAQ。 我尝试了各种优化措施 , 但效果均不明显 , 难道nlog(n)的倍增算法常数真的这么大吗? 其实仔细看看倍增的结构 , 你会发现 , 常数很小的样子 , 就是几个循环了事啊……

所以 , 这只能说明树剖的常数小 , 至于有多小 , 大家都可以脑补(某集训队论文分析 , 常数最大12) , 但如果查询的两个点很接近 , 那么可以快速出解。 但是倍增的时间复杂度的自由空间很小 , 所以在此题求lca一个理论上nlog(n)2的算法战胜了一个理论上nlog(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;}
0 0