[BZOJ4539][Hnoi2016]树(dfs序+主席树+lca)

211 篇文章 0 订阅
22 篇文章 0 订阅

题目描述

传送门

题解

可以看出在原树上的每一次复制都是一棵完整的子树,用dfs序表示出来就是一段连续的区间。
称原来给的大小为n的树为模板树,最后得到的树为答案树,把一次操作增加的节点看成一块,然后构成的树称为大树。
我们就需要将模板树和答案树结合起来计算答案。
答案树上两点的距离=两点分别走到所在块的根,在大树上走到两个块的lca,撤销进入块lca后两个点共同走过的路径。
第一步的答案就是找到点到它所在块的根在模板树中的距离
第二步,把大树上两点之间的边权定义为答案树上这两个块的根的距离,然后倍增lca求出
第三步,要知道两个点进入lca后具体是走到模板树上的哪个点,分别设为x,y,然后答案减去2*(lca(x,y)在模板树中的深度-lca块的根在模板树中的深度)
求大树中的lca都需要在模板树中找,因为在对应的块里大小关系是一定的,所以可以用主席树求区间k大来搞。

感觉这题的idea非常好,不过数据结构的码力还是太弱了,而且最后被long long的问题搞了好久,果然我还是太弱= =

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long

const int N=1e5+5;
const int E=N*2;
const int LOG=20;

int n,m,q,xx,yy,a,nn,size;LL number,b,x,y;
struct hp{LL len;int son1,son2;bool flag;};
struct hq{LL l,r;}interval[N];
LL Link233[N]; int block_root[N];
LL ans;

namespace Model_tree
{
    int h[N],father[N],in[N],out[N],val[N],root[N];
    int tot,point[N],nxt[E],v[E];
    int f[N][LOG+5];
    int sum[N*LOG*2],ls[N*LOG*2],rs[N*LOG*2];

    inline void addedge(int x,int y)
    {
        ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
        ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
    }
    inline void build_tree(int x,int fa,int dep)
    {
        h[x]=dep; father[x]=fa; in[x]=++nn; val[nn]=x;
        for (int i=1;i<LOG;++i)
            f[x][i]=f[f[x][i-1]][i-1];
        for (int i=point[x];i;i=nxt[i])
            if (v[i]!=fa)
            {
                f[v[i]][0]=x;
                build_tree(v[i],x,dep+1);
            }
        out[x]=nn;
    }
    inline int lca(int x,int y)
    {
        if (h[x]<h[y]) swap(x,y);
        for (int i=LOG-1;i>=0;--i)
            if (h[f[x][i]]>=h[y])
                x=f[x][i];
        if (x==y) return x;
        for (int i=LOG;i>=0;--i)
            if (f[x][i]!=f[y][i])
                x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    inline void build_pretree(int &now,int l,int r,int x)
    {
        int mid=(l+r)>>1;
        sum[++size]=sum[now]+1; ls[size]=ls[now]; rs[size]=rs[now]; now=size;
        if (l==r) return;
        if (x<=mid) build_pretree(ls[now],l,mid,x);
        else build_pretree(rs[now],mid+1,r,x);
    }
    inline int query_pretree(int lrange,int rrange,int l,int r,int k)
    {
        int mid=(l+r)>>1;
        if (l==r) return l;
        int t=sum[ls[rrange]]-sum[ls[lrange]];
        if (t>=k) return query_pretree(ls[lrange],ls[rrange],l,mid,k);
        else return query_pretree(rs[lrange],rs[rrange],mid+1,r,k-t);
    }
}
namespace Big_tree
{
    int h[N];
    int tot,point[N],nxt[E],v[E],c[E];
    int f[N][LOG+5]; LL s[N][LOG+5];

    inline void addedge(int x,int y,int z)
    {
        ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
        ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
    }
    inline void build_lca(int x,int fa,int dep)
    {
        h[x]=dep;
        for (int i=1;i<LOG;++i)
        {
            f[x][i]=f[f[x][i-1]][i-1];
            s[x][i]=s[x][i-1]+s[f[x][i-1]][i-1];
        }
        for (int i=point[x];i;i=nxt[i])
            if (v[i]!=fa)
            {
                f[v[i]][0]=x;
                s[v[i]][0]=(LL)c[i];
                build_lca(v[i],x,dep+1);
            }
    }
    inline hp lca(int x,int y)
    {
        LL sum=0;
        hp ans;
        if (h[x]<h[y]) swap(x,y);
        for (int i=LOG-1;i>=0;--i)
            if (h[f[x][i]]>h[y])
            {
                sum+=s[x][i];
                x=f[x][i];
            }
        if (f[x][0]==y)
        {
            sum+=s[x][0];
            return ans=(hp){sum,x,x,true};
        }
        if (h[x]!=h[y]) sum+=s[x][0],x=f[x][0];
        for (int i=LOG-1;i>=0;--i)
            if (f[x][i]!=f[y][i])
            {
                sum+=s[x][i]+s[y][i];
                x=f[x][i],y=f[y][i];
            }
        sum+=s[x][0]+s[y][0];
        return ans=(hp){sum,x,y,false};
    }
}

inline int find(int l,int r,LL x)
{
    int mid;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (x>=interval[mid].l&&x<=interval[mid].r) return mid;
        if (x<interval[mid].l) r=mid-1;
        else l=mid+1;
    }
    return mid;
}
inline int calc_len(int l,int r,LL x)
{
    int int_x=find(l,r,x);
    int rank_x=(int)(x-interval[int_x].l+1);
    int x_in_model=Model_tree::query_pretree(Model_tree::root[ Model_tree::in[ block_root[int_x] ]-1 ],Model_tree::root[ Model_tree::out[ block_root[int_x] ] ],1,n,rank_x);
    int ans=Model_tree::h[x_in_model]-Model_tree::h[block_root[int_x]];
    return ans;
}

int main()
{
    scanf("%d%d%d",&n,&m,&q);

    for (int i=1;i<n;++i)
    {
        scanf("%d%d",&xx,&yy);
        Model_tree::addedge(xx,yy);
    }
    Model_tree::build_tree(1,0,1);

    for (int i=1;i<=n;++i)
    {
        Model_tree::root[i]=Model_tree::root[i-1];
        Model_tree::build_pretree(Model_tree::root[i],1,n,Model_tree::val[i]);
    }

    interval[1].l=1; interval[1].r=(LL)n; block_root[1]=1;
    number=(LL)n;
    for (int i=1;i<=m;++i)
    {
        scanf("%d%lld",&a,&b);

        int int_size=Model_tree::out[a]-Model_tree::in[a]+1;
        interval[i+1].l=number+1; interval[i+1].r=number+(LL)int_size;
        number+=(LL)int_size;

        int int_b=find(1,i+1,b);
        Link233[i+1]=b; block_root[i+1]=a;
        int len=calc_len(1,i+1,b)+1;
        Big_tree::addedge(int_b,i+1,len);
    }

    Big_tree::build_lca(1,0,1);

    for (int i=1;i<=q;++i)
    {
        scanf("%lld%lld",&x,&y);

        int int_x=find(1,m+1,x),int_y=find(1,m+1,y);
        if (Big_tree::h[int_x]<Big_tree::h[int_y]) swap(x,y),swap(int_x,int_y);

        ans=(LL)calc_len(1,m+1,x)+(LL)calc_len(1,m+1,y);
        LL aa,bb;
        if (int_x!=int_y)
        {
            hp r=Big_tree::lca(int_x,int_y);
            ans+=r.len;
            if (r.flag)
            {
                aa=Link233[r.son1];
                bb=y;
            }
            else
            {
                aa=Link233[r.son1];
                bb=Link233[r.son2];
            }
        }
        else aa=x,bb=y;

        int inter=find(1,m+1,aa);
        int rank_a=(int)(aa-interval[inter].l+1);
        int rank_b=(int)(bb-interval[inter].l+1);
        int a_in_model=Model_tree::query_pretree(Model_tree::root[ Model_tree::in[ block_root[inter] ]-1 ],Model_tree::root[ Model_tree::out[ block_root[inter] ] ],1,n,rank_a);
        int b_in_model=Model_tree::query_pretree(Model_tree::root[ Model_tree::in[ block_root[inter] ]-1 ],Model_tree::root[ Model_tree::out[ block_root[inter] ] ],1,n,rank_b);
        int LCA=Model_tree::lca(a_in_model,b_in_model);
        ans-=(LL)(2*(Model_tree::h[LCA]-Model_tree::h[block_root[inter]]));

        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值