zoukankan      html  css  js  c++  java
  • 【学习笔记 边分树】【uoj400】【CTSC2018】暴力写挂

    题目

    描述

    ​ 有两棵树(T)(T'),节点个数都为(n),根节点都为(1)号节点;

    ​ 求两两点之间

    [ egin{align} depth(x) + depth(y) - depth(LCA(x,y)) - depth'(LCA'(x,y)) end{align} \ 其中depth(x)为x和1号节点的树上距离 \ ]

    ​ 的最大值;

    范围

    ​ $1 le n le 366666 $ ;

    题解

    • 原式 = (frac{(dep(x) + dep(y) + dis(x,y)) }{2} -dep'(x,y)) ;

    • 枚举第二颗树的节点(u),只需要查询(u)的各个子树之间的答案;

    • 考虑查询一个对很多点的左边的式子时,可以直接用边分治;

    • 询问两个子树之间的答案也可以用边分治,复杂度是(O(n^2log n))的;

    • 注意到边分树是一个二叉树的结构,直接采用线段树和并的写法合并;

    • 学习了一下边分治:

      • 每次选择一条边((u,v)),使得两边的子树最大(size)最小,不断分治;
      • 考虑直接分析复杂度:
        • 这部分理论是(cjl)老师在济南讲课的时候说的,网上说了这个的好少。。。。
        • 设整棵树最大的度数为(D)(u)的子树大小为(s),不妨令:(u ge n - s);
        • (u)的子树大小最大的儿子的子树大小是(p)
        • 直接有:$p ge frac{s-1}{D-1} $ ;
        • 同时由于((u,v))是最优的决策,一定有:$n - p ge s $;
        • 解得:$s le frac{n(D-1) + 1}{D} $ ;
        • 意思是每次的联通块大小是以约(frac{D-1}{D})的倍数缩小;
      • (D)是个常数时复杂度是(log)的,所以直接考虑将图变成一颗二叉树;
        • 黑色的点和边代表原图中的边,绿色的点和边代表虚点和虚边;
          -新图的大小为原图的两倍;

        • 注意对边权和点权的修改因题而异;

    • 另外意外看到(dsu on tree)

      • 适用于一些删除比较麻烦的树上数据结构,不支持修改;
      • (dfs)并树剖,当一个节点做完了存储了子树的信息;
      • 假设我们有一个支持插入和完全清零的全局数据结构(Ds),一次操作是(O(Ds)的);
      • 每次先做轻儿子,做完后枚举子树节点暴力清空和轻儿子子树有关的(Ds)信息;
      • 所以当做完(u)的所有儿子回溯到(u)时,(Ds)中只存在(u)的重儿子的子树的影响;
      • 再做一遍所有轻儿子的子树,插入到(Ds)中去;
      • 当一个点被遍历的时候一定是出现了一条轻链;
      • 一个点向上的轻链个数为(O(log n))的,所以为(O(n log n Ds))
      • 套用到本题得到一个(O(n log^2 n))的做法;

    //这份代码过不了uoj的extra test ,luogu上会T一个点,暂时还没有修改;

    include<bits/stdc++.h>

    define mk make_pair

    define fi first

    define se second

    define pb push_back

    define inf 1e18

    define ll long long

    using namespace std;
    const int N=366666<<2,M=22;
    int n,o,hd[N],cnt,rt,mn,Rt[N],ls[N],rs[N],fa[N];
    int vis[N],size,sz[N],d[N],tot,lc[NM],rc[NM];
    ll dep[N],dis[M][N],val[N],mxl[NM],mxr[NM],wv[NM],ans=-inf;
    struct Edge{int v,nt,w;}E[N];
    typedef pair<int,int>pii;
    vectorg[N];
    char gc(){
    static char
    p1,p2,s[1000000];
    if(p1p2)p2=(p1=s)+fread(s,1,1000000,stdin);
    return(p1
    p2)?EOF:
    p1++;
    }
    int rd(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c'-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=x10+c-'0';c=gc();}
    return x
    f;
    }
    void upd(ll&x,ll y){if(x<y)x=y;}
    void adde(int u,int v,int w){
    E[o]=(Edge){v,hd[u],w};hd[u]=o++;
    E[o]=(Edge){u,hd[v],w};hd[v]=o++;
    }
    void dfs1(int u,int F){
    for(int i=0,lst=0;i<(int)g[u].size();++i){
    int v=g[u][i].fi,w=g[u][i].se;
    if(v
    F)continue;
    dep[v]=dep[u]+w;dfs1(v,u);
    if(!lst){adde(u,v,w);lst=u;continue;}
    adde(lst,++cnt,0);adde(cnt,v,w);lst=cnt;
    }
    }
    void get_rt(int u,int F){
    sz[u]=1;
    for(int i=hd[u];~i;i=E[i].nt){
    int v=E[i].v;
    if(vis[i]||vF)continue;
    get_rt(v,u);sz[u]+=sz[v];
    int t=max(size-sz[v],sz[v]);
    if(mn>=t)rt=i,mn=t;
    }
    }
    void calc(int d,int u,int F,ll now){
    dis[d][u]=now;
    for(int i=hd[u];~i;i=E[i].nt){
    int v=E[i].v;
    if(vis[i]||v
    F)continue;
    calc(d,v,u,now+E[i].w);
    }
    }
    int solve(int u,int F){
    if(size1){fa[u]=F;return u;}
    int now=++cnt;
    rt=-1;mn=size;get_rt(u,0);
    val[now]=E[rt].w;
    vis[rt]=vis[rt^1]=1;
    d[now]=d[fa[now]=F]+1;
    int v1=E[rt].v,v2=E[rt^1].v;
    calc(d[now],v1,0,0);
    calc(d[now],v2,0,0);
    size-=sz[v1];rs[now]=solve(v2,now);
    size=sz[v1];ls[now]=solve(v1,now);
    return now;
    }
    int ins(int x){
    for(int u=fa[x],v=x,lst=0;u;v=u,u=fa[u]){
    wv[++tot]=val[u];mxl[tot]=mxr[tot]=-inf;
    if(ls[u]
    v)mxl[tot]=dep[x]+dis[d[u]][x],lc[tot]=lst;
    else mxr[tot]=dep[x]+dis[d[u]][x],rc[tot]=lst;
    lst=tot;
    }
    return tot;
    }
    int merge(int x,int y,ll&tmp){
    if(!x||!y)return x+y;
    upd(tmp,wv[x]+max(mxl[x]+mxr[y],mxl[y]+mxr[x]));
    mxl[x]=max(mxl[x],mxl[y]);
    mxr[x]=max(mxr[x],mxr[y]);
    lc[x]=merge(lc[x],lc[y],tmp);
    rc[x]=merge(rc[x],rc[y],tmp);
    return x;
    }
    void dfs2(int u,int F,ll now){
    Rt[u]=ins(u);
    upd(ans,dep[u]-now);
    ll tmp=-inf;
    for(int i=hd[u];~i;i=E[i].nt){
    int v=E[i].v;
    if(v==F)continue;
    dfs2(v,u,now+E[i].w);
    Rt[u]=merge(Rt[u],Rt[v],tmp);
    }
    upd(ans,(tmp>>1)-now);
    }
    int main(){
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    cnt=n=rd();
    memset(hd,-1,sizeof(hd));
    for(int i=1,u,v,w;i<n;++i){
    u=rd(),v=rd(),w=rd();
    g[u].pb(mk(v,w));
    g[v].pb(mk(u,w));
    }
    dfs1(1,0);
    mxl[0]=mxr[0]=wv[0]=-inf;
    size=cnt;solve(1,0);
    o=0;memset(hd,-1,sizeof(hd));
    for(int i=1;i<n;++i){
    int u=rd(),v=rd(),w=rd();
    adde(u,v,w);
    }
    dfs2(1,0,0);
    cout<<ans<<endl;
    return 0;
    }

  • 相关阅读:
    前缀和问题
    AtCoder Beginner Contest 085(ABCD)
    73.链表的基本操作
    112、文本串的加密
    100.容器List-ArrayList
    GUI颜色、字体设置对话框
    (贪心)多机调度问题
    POJ-1700 Crossing River
    lower_bound() upper_bound()函数
    HDU 1141
  • 原文地址:https://www.cnblogs.com/Paul-Guderian/p/10652758.html
Copyright © 2011-2022 走看看