[BZOJ5341][CTSC2018]暴力写挂-边分治树合并

暴力写挂

Description

temporaryDO 是一个很菜的 OIer 。在 4 月,他在省队选拔赛的考场上见到了《林克卡特树》一题,其中 k = 0k=0 的部分分是求树 TT 上的最长链。可怜的 temporaryDO 并不会做这道题,他在考场上抓猫耳挠猫腮都想不出一点思路。

这时,善良的板板出现在了空中,他的身上发出璀璨却柔和的光芒,荡漾在考场上。“题目并不难。” 板板说。那充满磁性的声音,让 temporaryDO 全身充满了力量。

他决定:写一个枚举点对求 LCA 算距离的 k = 0 O ( n 2   l o g   n ) 的部分分程序!于是, temporaryDO 选择以 11 为根,建立了求 LCA 的树链剖分结构,然后写了二重 for 循环枚举点对。

然而,菜菜的 temporaryDO 不小心开小了数组,于是数组越界到了一片神秘的内存区域。但恰好的是,那片内存区域存储的区域恰好是另一棵树 T 。这样一来,程序并没有 RE ,但他求 xx 和 yy 的距离的时候,计算的是

d e p t h ( x ) + d e p t h ( y ) ( d e p t h ( L C A ( x , y ) ) + d e p t h ( L C A ( x , y ) ) )

最后程序会输出每一对点对 i , j ( i j ) 的如上定义的“距离” 的最大值。 temporaryDO 的程序在评测时光荣地爆零了。但他并不服气,他决定花好几天把自己的程序跑出来。请你根据 T T 帮帮可怜的 temporaryDO 求出他程序的输出。

Input

第一行包含一个整数 n ,表示树上的节点个数;
2 到第 n 行,每行三个整数 x , y , v ,表示 T 中存在一条从 x y 的边,其长度为 v ;第 n + 1 到第 2 n 1 ,每行三个整数 x , y , v ,表示 T 中存在一条从 x y 的边,其长度为 v

Output

输出一行一个整数,表示 temporaryDO 的程序的输出。

Sample Input

6
1 2 2
1 3 0
2 4 1
2 5 -7
3 6 0
1 2 -1
2 3 -1
2 5 3
2 6 -2
3 4 8

Sample Output

5

HINT

点对 (3, 4)(3,4) 的距离计算为 3 + 0 - (0 + (-2)) = 53+0−(0+(−2))=5 。

对于所有数据, n 366666 , | v | 2017011328

depth(p) 和 depth’(p) 分别表示树 T T 中点 1 到点 p 的距离,这里规定,距离指的是经过的边的边权总和,其中 d e p t h ( 1 ) = 0 。 LCA(x, y) 和 LCA’(x, y) 分别表示树 T T 中点 xx 与点 y 的最近公共祖先,即在从 x y 的最短路径上的距离根经过边数最少的点。


猫式树上最优化问题系列的特性:写正解的性价比远低于乱搞做法……
沉迷于颓废导致写了一上午假做法却没意识到,最后又花了一下午写正解……


思路:
首先你得知道什么是边分治,并且知道这题要用。

然后想必各种思路就出来了。

首先说说咱的假做法。
这个第二棵树的 l c a 好像不太兹瓷化简,那么考虑枚举第二颗树的 l c a
于是化简第一棵树相关内容:

d e p t h ( x ) + d e p t h ( y ) d e p t h ( L C A ( x , y ) ) = 1 2 ( d e p t h ( x ) + d e p t h ( y ) + d i s t a n c e ( x , y ) )

突然舒服.jpg

然后就可以搞事了。
考虑建出一棵边分树,用它维护一些信息。

对于一条分治过程中的边 p = u p , v p ,设它将当前分治子树分成两个点集 U , V ,那么需要维护两个信息:
l m a x [ p ] = m a x ( d e p [ u ] + d i s t ( u , u p ) | u U ) r m a x [ p ] = m a x ( d e p [ v ] + d i s t ( v , v p ) | v V ) .

这样便可支持 O ( l o g n ) 询问一个点与一个点集之间的最大方案。
具体方法是,找到深度最深的一条以询问点为端点的边,暴力向上跳,每次在每层的另一个点集处询问最大值并更新答案即可。

于是就可以考虑遍历第二棵树的每个节点作为 l c a 并计算贡献了。
考虑难以在点分树上删除,使用树上启发式合并维护,即继承重儿子的点集,而当遍历完非重儿子时,直接清空整棵边分树即可。

复杂度 O ( n l o g 2 n ) ,用尽全力卡常极限数据也要 8.0 s ……

接下来是正解:

为什么要启发式合并呢?
听说过线段树合并么?
可以发现边分树是二叉的,形如线段树,那么写个边分树合并不就好了!

答案可以方便地在合并过程中统计。
复杂度 O ( n l o g n ) ,快如……好吧也没比 O ( n l o g 2 n ) 快多少……
一个 3 e 5 l o g 只快了四倍多,这丢人的常数……不过还是能过的

放代码。
首先是本zz的85分 O ( n l o g 2 n ) 辣鸡做法:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

typedef long long ll;
typedef pair<int,int> pr;
const int N=377779;
const int K=23;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x*f;
}

template<typename T> inline bool chkmin(T &a,T b){if(a>b){a=b;return 1;}return 0;}
template<typename T> inline bool chkmax(T &a,T b){if(a<b){a=b;return 1;}return 0;}

int to[N<<1],nxt[N<<1],w[N<<1],beg[N],tot;
int n,id[N],ed[N],seg[N],dfn;
int siz[N],son[N],fa[N];
ll dep[N],ans=-1e18;

inline void add(int u,int v,int c)
{
    to[++tot]=v;nxt[tot]=beg[u];beg[u]=tot;w[tot]=c;
}

namespace tdc
{
    const int P=N<<1;
    int to[P<<1],nxt[P<<1],w[P<<1],beg[P],tot=1;
    int st[P<<1][K],id[P],bin[P<<1],depth[P],dfn;
    int n,siz[P],ban[P<<1],bel[P];
    ll dep[P],lmx[P<<1],rmx[P<<1];
    int fat[P<<1],son[P<<1];
    vector<pr> g[P];

    inline void add(int u,int v,int c)
    {
        to[++tot]=v;nxt[tot]=beg[u];beg[u]=tot;w[tot]=c;
    }

    inline void dfs_init(int u,int fa)
    {
        st[id[u]=++dfn][0]=u;
        for(int i=beg[u];i;i=nxt[i])
            if(to[i]!=fa)
            {
                dep[to[i]]=dep[u]+w[i];
                depth[to[i]]=depth[u]+1;
                dfs_init(to[i],u);
                st[++dfn][0]=u;
            }
    }

    inline void build_lca()
    {
        for(int i=2;i<=dfn;i++)
            bin[i]=bin[i>>1]+1;
        for(int i=1;i<=bin[dfn];i++)
            for(int j=1;j+(1<<i)-1<=dfn;j++)
            {
                if(depth[st[j][i-1]]<depth[st[j+(1<<i-1)][i-1]])
                    st[j][i]=st[j][i-1];
                else
                    st[j][i]=st[j+(1<<i-1)][i-1];
            }
    }

    inline int lca(int a,int b)
    {
        a=id[a],b=id[b];if(a>b)swap(a,b);
        int dlt=bin[b-a+1];
        return depth[st[a][dlt]]<depth[st[b-(1<<dlt)+1][dlt]]?st[a][dlt]:st[b-(1<<dlt)+1][dlt];
    }

    inline ll dist(int a,int b)
    {
        return dep[a]+dep[b]-(dep[lca(a,b)]<<1);
    }

    inline void build_tree(int u,int fa)
    {
        int lst=0;
        for(int i=0;i<g[u].size();i++)
            if(g[u][i].first!=fa)
            {
                if(!lst)
                {
                    add(u,g[u][i].first,g[u][i].second);
                    add(g[u][i].first,u,g[u][i].second);
                    lst=u;
                }
                else
                {
                    add(lst,++n,0);add(n,lst,0);lst=n;
                    add(lst,g[u][i].first,g[u][i].second);
                    add(g[u][i].first,lst,g[u][i].second);
                }
                build_tree(g[u][i].first,u);
            }
    }

    inline int dfs_find(int u,int fa,int &rt,int &dlt,int tsz)
    {
        int sz=1;
        for(int i=beg[u];i;i=nxt[i])
            if(!ban[i] && to[i]!=fa)
            {
                sz+=(siz[i]=dfs_find(to[i],u,rt,dlt,tsz));
                siz[i^1]=tsz-siz[i];
                if(chkmin(dlt,abs(siz[i^1]-siz[i])))
                    rt=i;
            }
        return sz;
    }

    inline void work(int u,int tsz,int fa,int l)
    {
        if(tsz==1)return;
        int rt=0,dlt=1e9+7;
        dfs_find(u,0,rt,dlt,tsz);
        ban[rt]=ban[rt^1]=1;
        fat[rt]=fa;son[rt]=l;
        lmx[rt]=rmx[rt]=-1e18;
        bel[to[rt]]=rt;bel[to[rt^1]]=rt;
        work(to[rt],siz[rt],rt,0);
        work(to[rt^1],siz[rt^1],rt,1);
    }

    inline void init()
    {
        build_tree(1,0);
        dfs_init(1,0);
        build_lca();
        work(1,n,0,0);
    }

    inline void del(int u)
    {
        int p=bel[u],dir;
        if(to[p]==u)
            lmx[p]=-1e18;
        else 
            rmx[p]=-1e18;
        for(dir=son[p],p=fat[p];p;dir=son[p],p=fat[p])
            (!dir?lmx[p]:rmx[p])=-1e18;
    }

    inline void add(int u)
    {
        int p=bel[u],dir;
        if(to[p]==u)
            chkmax(lmx[p],dep[u]);
        else 
            chkmax(rmx[p],dep[u]);
        for(dir=son[p],p=fat[p];p;dir=son[p],p=fat[p])
            chkmax((!dir?lmx[p]:rmx[p]),dist(u,to[p^dir])+dep[u]);
    }

    inline ll query(int u)
    {
        int p=bel[u],dir;ll ret=-1e18;
        chkmax(ret,lmx[p]+dep[u]+(to[p]!=u)*w[p]);
        chkmax(ret,rmx[p]+dep[u]+(to[p]==u)*w[p]);
        for(dir=son[p],p=fat[p];p!=0;dir=son[p],p=fat[p])
            chkmax(ret,dep[u]+(!dir?rmx[p]:lmx[p])+dist(to[p^1^dir],u));
        return ret;
    }
}

inline void dfs_pre(int u,int fa)
{
    seg[id[u]=++dfn]=u;siz[u]=1;
    for(int i=beg[u];i;i=nxt[i])
        if(to[i]!=fa)
        {
            dep[to[i]]=dep[u]+w[i];
            dfs_pre(to[i],u);
            siz[u]+=siz[to[i]];
            if(!son[u] || siz[son[u]]<siz[to[i]])
                son[u]=to[i];
        }
    ed[u]=dfn;
}

inline void dfs_work(int u,int fa)
{
    for(int i=beg[u];i;i=nxt[i])
        if(to[i]!=fa && to[i]!=son[u])
            dfs_work(to[i],u);
    if(son[u])dfs_work(son[u],u);
    tdc::add(u);

    for(int i=beg[u];i;i=nxt[i])
        if(to[i]!=fa && to[i]!=son[u])
        {
            for(int j=id[to[i]];j<=ed[to[i]];j++)
                chkmax(ans,tdc::query(seg[j])/2-dep[u]);
            for(int j=id[to[i]];j<=ed[to[i]];j++)
                tdc::add(seg[j]);
        }
    chkmax(ans,tdc::query(u)/2-dep[u]);
    if(fa && son[fa]!=u)
        for(int i=id[u];i<=ed[u];i++)
            tdc::del(seg[i]);
}

int main()
{
    tdc::n=n=read();
    for(int i=1,a,b,c;i<n;i++)
    {
        a=read();b=read();c=read();
        tdc::g[a].push_back(pr(b,c));
        tdc::g[b].push_back(pr(a,c));
    }

    for(int i=1,a,b,c;i<n;i++)
    {
        a=read();b=read();c=read();
        add(a,b,c);add(b,a,c);
    }

    tdc::init();
    dfs_pre(1,0);
    dfs_work(1,0);

    printf("%lld\n",ans);
    return 0;
}

然后是神奇的100分 O ( n l o g n ) 做法:(感谢xzk dalao 的提示不然咱可能一辈子85pts)

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

typedef long long ll;
typedef pair<int,int> pr;
const int N=377779;
const int K=23;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x*f;
}

template<typename T> inline bool chkmin(T &a,T b){if(a>b){a=b;return 1;}return 0;}
template<typename T> inline bool chkmax(T &a,T b){if(a<b){a=b;return 1;}return 0;}

int n,siz[N],fa[N],rt[N];
ll dep[N],ans=0;
vector<pr> g[N];

namespace tdc
{
    const int P=N<<1;
    const int M=N*20;

    int to[P<<1],nxt[P<<1],w[P<<1],beg[P],tot=1;
    int st[P<<1][K],id[P],bin[P<<1],depth[P],dfn;
    int n,siz[P<<1],ban[P<<1],bel[P],son[P<<1][2];
    vector<pr> g[P];
    ll dep[P];

    int stk[M],top;
    ll lmx[M],rmx[M];
    int cnt,fat[M],ind[M],ls[M],rs[M];

    inline void add(int u,int v,int c)
    {
        to[++tot]=v;nxt[tot]=beg[u];beg[u]=tot;w[tot]=c;
    }

    inline void dfs_init(int u,int fa)
    {
        st[id[u]=++dfn][0]=u;
        for(int i=beg[u];i;i=nxt[i])
            if(to[i]!=fa)
            {
                dep[to[i]]=dep[u]+w[i];
                depth[to[i]]=depth[u]+1;
                dfs_init(to[i],u);
                st[++dfn][0]=u;
            }
    }

    inline void build_lca()
    {
        for(int i=2;i<=dfn;i++)
            bin[i]=bin[i>>1]+1;
        for(int i=1;i<=bin[dfn];i++)
            for(int j=1;j+(1<<i)-1<=dfn;j++)
            {
                if(depth[st[j][i-1]]<depth[st[j+(1<<i-1)][i-1]])
                    st[j][i]=st[j][i-1];
                else
                    st[j][i]=st[j+(1<<i-1)][i-1];
            }
    }

    inline int lca(int a,int b)
    {
        a=id[a],b=id[b];if(a>b)a^=b,b^=a,a^=b;int dlt=bin[b-a+1];
        return depth[st[a][dlt]]<depth[st[b-(1<<dlt)+1][dlt]]?st[a][dlt]:st[b-(1<<dlt)+1][dlt];
    }

    inline ll dist(int a,int b)
    {
        return dep[a]+dep[b]-(dep[lca(a,b)]<<1);
    }

    inline void build_tree(int u,int fa)
    {
        int lst=0;
        for(int i=0;i<g[u].size();i++)
            if(g[u][i].first!=fa)
            {
                if(!lst)
                {
                    add(u,g[u][i].first,g[u][i].second);
                    add(g[u][i].first,u,g[u][i].second);
                    lst=u;
                }
                else
                {
                    add(lst,++n,0);add(n,lst,0);lst=n;
                    add(lst,g[u][i].first,g[u][i].second);
                    add(g[u][i].first,lst,g[u][i].second);
                }
                build_tree(g[u][i].first,u);
            }
    }

    inline int dfs_find(int u,int fa,int &rt,int &dlt,int tsz)
    {
        int sz=1;
        for(int i=beg[u];i;i=nxt[i])
            if(!ban[i] && to[i]!=fa)
            {
                sz+=(siz[i]=dfs_find(to[i],u,rt,dlt,tsz));
                siz[i^1]=tsz-siz[i];
                if(chkmin(dlt,abs(siz[i^1]-siz[i])))
                    rt=i;
            }
        return sz;
    }

    inline int work(int u,int tsz,int fa)
    {
        if(tsz==1)return 0;
        int rt=0,dlt=1e9+7;
        dfs_find(u,0,rt,dlt,tsz);

        ban[rt]=ban[rt^1]=1;fat[rt]=fa;
        lmx[rt]=rmx[rt]=-1e18;
        bel[to[rt]]=rt;bel[to[rt^1]]=rt;

        son[rt][0]=work(to[rt],siz[rt],rt);
        son[rt][1]=work(to[rt^1],siz[rt^1],rt);
        return rt;
    }

    inline void init()
    {
        build_tree(1,0);
        dfs_init(1,0);
        build_lca();
        work(1,n,0);
        for(int i=1;i<M;i++)
            stk[++top]=i;
    }

    inline int newnode(int p)
    {
        int ret=stk[top--];
        lmx[ret]=rmx[ret]=-1e18;
        ls[ret]=rs[ret]=0;
        ind[ret]=p;return ret;
    }

    inline int build(int u)
    {
        int now=0;
        for(int p=bel[u],dir=(to[p]!=u),ch=0;p;ch=now,p=fat[p])
        {
            now=newnode(p);
            chkmax((!dir?lmx[now]:rmx[now]),dist(u,to[p^dir])+dep[u]);
            if(ch)(son[p][1]==ind[ch]?rs[now]:ls[now])=ch;
            dir=son[fat[p]][1]==p;
        }
        return now;
    }

    inline int merge(int x,int y,ll p)
    {
        if(!x || !y)return x|y;
        chkmax(ans,(lmx[x]+rmx[y]+w[ind[x]])/2-p);
        chkmax(ans,(rmx[x]+lmx[y]+w[ind[x]])/2-p);
        chkmax(lmx[x],lmx[y]);ls[x]=merge(ls[x],ls[y],p);
        chkmax(rmx[x],rmx[y]);rs[x]=merge(rs[x],rs[y],p);
        if(y)stk[++top]=y;return x;
    }
}

inline void dfs_pre(int u,int fa)
{
    for(int i=0,v;i<g[u].size();i++)
        if((v=g[u][i].first)!=fa)
        {
            dep[v]=dep[u]+g[u][i].second;
            dfs_pre(v,u);
            siz[u]+=siz[v];
        }
}

inline bool cmp(pr a,pr b)
{
    return siz[a.first]<siz[b.first];
}

inline void dfs_work(int u,int fa)
{
    rt[u]=tdc::build(u);
    chkmax(ans,tdc::dep[u]-dep[u]);
    sort(g[u].begin(),g[u].end(),cmp);
    for(int i=0,v;i<g[u].size();i++)
        if((v=g[u][i].first)!=fa)
        {
            dfs_work(v,u);
            rt[u]=tdc::merge(rt[u],rt[v],dep[u]);
        }
}

int main()
{
    tdc::n=n=read();
    for(int i=1,a,b,c;i<n;i++)
    {
        a=read();b=read();c=read();
        tdc::g[a].push_back(pr(b,c));
        tdc::g[b].push_back(pr(a,c));
    }

    for(int i=1,a,b,c;i<n;i++)
    {
        a=read();b=read();c=read();
        g[a].push_back(pr(b,c));
        g[b].push_back(pr(a,c));
    }

    tdc::init();
    dfs_pre(1,0);
    dfs_work(1,0);

    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zlttttt/article/details/80400176